aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/advancedDetector.cpp59
-rw-r--r--engines/advancedDetector.h1
-rw-r--r--engines/agi/agi.cpp48
-rw-r--r--engines/agi/agi.h14
-rw-r--r--engines/agi/console.cpp2
-rw-r--r--engines/agi/cycle.cpp4
-rw-r--r--engines/agi/detection.cpp1
-rw-r--r--engines/agi/id.cpp4
-rw-r--r--engines/agi/keyboard.cpp1
-rw-r--r--engines/agi/loader_v2.cpp5
-rw-r--r--engines/agi/loader_v3.cpp2
-rw-r--r--engines/agi/objects.cpp18
-rw-r--r--engines/agi/op_cmd.cpp14
-rw-r--r--engines/agi/op_dbg.cpp20
-rw-r--r--engines/agi/preagi_mickey.cpp6
-rw-r--r--engines/agi/preagi_winnie.cpp1
-rw-r--r--engines/agi/predictive.cpp2
-rw-r--r--engines/agi/saveload.cpp3
-rw-r--r--engines/agi/sound_2gs.cpp19
-rw-r--r--engines/agi/sound_coco3.cpp4
-rw-r--r--engines/agi/sound_midi.cpp3
-rw-r--r--engines/agi/sound_sarien.cpp7
-rw-r--r--engines/agi/words.cpp4
-rw-r--r--engines/agos/agos.cpp4
-rw-r--r--engines/agos/agos.h13
-rw-r--r--engines/agos/animation.cpp4
-rw-r--r--engines/agos/debug.cpp72
-rw-r--r--engines/agos/event.cpp3
-rw-r--r--engines/agos/menus.cpp2
-rw-r--r--engines/agos/midi.cpp8
-rw-r--r--engines/agos/res.cpp11
-rw-r--r--engines/agos/res_snd.cpp19
-rw-r--r--engines/agos/rooms.cpp42
-rw-r--r--engines/agos/saveload.cpp6
-rw-r--r--engines/agos/script_dp.cpp2
-rw-r--r--engines/agos/script_ff.cpp6
-rw-r--r--engines/agos/script_pn.cpp2
-rw-r--r--engines/agos/script_pp.cpp9
-rw-r--r--engines/agos/sound.cpp55
-rw-r--r--engines/agos/string.cpp163
-rw-r--r--engines/agos/subroutine.cpp20
-rw-r--r--engines/agos/verb_pn.cpp2
-rw-r--r--engines/agos/vga.cpp4
-rw-r--r--engines/agos/vga_ff.cpp4
-rw-r--r--engines/cine/anim.cpp4
-rw-r--r--engines/cine/bg.cpp2
-rw-r--r--engines/cine/cine.cpp3
-rw-r--r--engines/cine/cine.h8
-rw-r--r--engines/cine/console.cpp (renamed from engines/gob/helper.h)23
-rw-r--r--engines/cine/console.h50
-rw-r--r--engines/cine/main_loop.cpp6
-rw-r--r--engines/cine/module.mk1
-rw-r--r--engines/cine/object.cpp2
-rw-r--r--engines/cine/saveload.cpp2
-rw-r--r--engines/cine/script_fw.cpp2
-rw-r--r--engines/cine/sound.cpp2
-rw-r--r--engines/cruise/background.cpp3
-rw-r--r--engines/cruise/cell.cpp4
-rw-r--r--engines/cruise/cruise.cpp2
-rw-r--r--engines/cruise/cruise.h2
-rw-r--r--engines/cruise/cruise_main.cpp30
-rw-r--r--engines/cruise/dataLoader.cpp3
-rw-r--r--engines/cruise/decompiler.cpp20
-rw-r--r--engines/cruise/function.cpp7
-rw-r--r--engines/cruise/object.cpp11
-rw-r--r--engines/cruise/overlay.cpp3
-rw-r--r--engines/cruise/saveload.cpp3
-rw-r--r--engines/cruise/script.cpp7
-rw-r--r--engines/cruise/sound.cpp1
-rw-r--r--engines/cruise/vars.cpp4
-rw-r--r--engines/cruise/vars.h4
-rw-r--r--engines/dialogs.cpp43
-rw-r--r--engines/draci/animation.cpp2
-rw-r--r--engines/draci/barchive.cpp2
-rw-r--r--engines/draci/console.cpp43
-rw-r--r--engines/draci/console.h50
-rw-r--r--engines/draci/detection.cpp9
-rw-r--r--engines/draci/draci.cpp15
-rw-r--r--engines/draci/draci.h12
-rw-r--r--engines/draci/game.cpp16
-rw-r--r--engines/draci/module.mk1
-rw-r--r--engines/draci/mouse.cpp8
-rw-r--r--engines/draci/saveload.cpp4
-rw-r--r--engines/draci/screen.cpp2
-rw-r--r--engines/draci/script.cpp4
-rw-r--r--engines/draci/script.h4
-rw-r--r--engines/draci/sound.cpp19
-rw-r--r--engines/draci/sprite.cpp2
-rw-r--r--engines/draci/walking.cpp4
-rw-r--r--engines/drascula/detection.cpp13
-rw-r--r--engines/drascula/drascula.cpp2
-rw-r--r--engines/drascula/drascula.h4
-rw-r--r--engines/drascula/resource.cpp10
-rw-r--r--engines/drascula/rooms.cpp4
-rw-r--r--engines/drascula/saveload.cpp4
-rw-r--r--engines/drascula/sound.cpp2
-rw-r--r--engines/engine.cpp31
-rw-r--r--engines/engine.h29
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/game.h1
-rw-r--r--engines/gob/console.cpp163
-rw-r--r--engines/gob/console.h58
-rw-r--r--engines/gob/dataio.cpp676
-rw-r--r--engines/gob/dataio.h127
-rw-r--r--engines/gob/demos/demoplayer.cpp2
-rw-r--r--engines/gob/detection_tables.h10
-rw-r--r--engines/gob/draw.cpp14
-rw-r--r--engines/gob/draw_playtoons.cpp2
-rw-r--r--engines/gob/draw_v1.cpp4
-rw-r--r--engines/gob/draw_v2.cpp4
-rw-r--r--engines/gob/game.cpp23
-rw-r--r--engines/gob/gob.cpp9
-rw-r--r--engines/gob/gob.h16
-rw-r--r--engines/gob/goblin.cpp142
-rw-r--r--engines/gob/goblin_v1.cpp52
-rw-r--r--engines/gob/goblin_v2.cpp302
-rw-r--r--engines/gob/goblin_v4.cpp126
-rw-r--r--engines/gob/hotspots.cpp13
-rw-r--r--engines/gob/init.cpp87
-rw-r--r--engines/gob/inter.cpp35
-rw-r--r--engines/gob/inter_bargon.cpp4
-rw-r--r--engines/gob/inter_playtoons.cpp26
-rw-r--r--engines/gob/inter_v1.cpp74
-rw-r--r--engines/gob/inter_v2.cpp63
-rw-r--r--engines/gob/inter_v4.cpp4
-rw-r--r--engines/gob/inter_v6.cpp16
-rw-r--r--engines/gob/map.cpp451
-rw-r--r--engines/gob/map.h193
-rw-r--r--engines/gob/map_v1.cpp32
-rw-r--r--engines/gob/map_v2.cpp31
-rw-r--r--engines/gob/module.mk1
-rw-r--r--engines/gob/resources.cpp27
-rw-r--r--engines/gob/resources.h10
-rw-r--r--engines/gob/save/saveconverter.cpp1
-rw-r--r--engines/gob/save/saveconverter.h2
-rw-r--r--engines/gob/save/savefile.cpp1
-rw-r--r--engines/gob/save/savefile.h37
-rw-r--r--engines/gob/script.cpp25
-rw-r--r--engines/gob/script.h2
-rw-r--r--engines/gob/sound/adlib.cpp2
-rw-r--r--engines/gob/sound/cdrom.cpp8
-rw-r--r--engines/gob/sound/cdrom.h4
-rw-r--r--engines/gob/sound/sound.cpp29
-rw-r--r--engines/gob/sound/sounddesc.cpp2
-rw-r--r--engines/gob/totfile.cpp4
-rw-r--r--engines/gob/util.cpp6
-rw-r--r--engines/gob/variables.cpp4
-rw-r--r--engines/gob/video.cpp4
-rw-r--r--engines/gob/videoplayer.cpp7
-rw-r--r--engines/gob/videoplayer.h2
-rw-r--r--engines/groovie/font.cpp4
-rw-r--r--engines/groovie/groovie.cpp1
-rw-r--r--engines/groovie/groovie.h2
-rw-r--r--engines/groovie/music.cpp2
-rw-r--r--engines/groovie/resource.cpp1
-rw-r--r--engines/groovie/saveload.cpp1
-rw-r--r--engines/groovie/saveload.h1
-rw-r--r--engines/groovie/script.cpp4
-rw-r--r--engines/hugo/console.cpp (renamed from engines/hugo/engine.h)27
-rw-r--r--engines/hugo/console.h50
-rw-r--r--engines/hugo/detection.cpp7
-rw-r--r--engines/hugo/display.cpp188
-rw-r--r--engines/hugo/display.h33
-rw-r--r--engines/hugo/display_v1d.cpp33
-rw-r--r--engines/hugo/display_v1w.cpp21
-rw-r--r--engines/hugo/engine.cpp968
-rw-r--r--engines/hugo/file.cpp370
-rw-r--r--engines/hugo/file.h39
-rw-r--r--engines/hugo/file_v1d.cpp29
-rw-r--r--engines/hugo/file_v1w.cpp8
-rw-r--r--engines/hugo/file_v2d.cpp45
-rw-r--r--engines/hugo/file_v3d.cpp49
-rw-r--r--engines/hugo/game.h153
-rw-r--r--engines/hugo/global.h1
-rw-r--r--engines/hugo/hugo.cpp1400
-rw-r--r--engines/hugo/hugo.h165
-rw-r--r--engines/hugo/intro.cpp2
-rw-r--r--engines/hugo/intro.h16
-rw-r--r--engines/hugo/intro_v1d.cpp74
-rw-r--r--engines/hugo/intro_v1w.cpp9
-rw-r--r--engines/hugo/intro_v2d.cpp12
-rw-r--r--engines/hugo/intro_v2w.cpp2
-rw-r--r--engines/hugo/intro_v3d.cpp36
-rw-r--r--engines/hugo/intro_v3w.cpp30
-rw-r--r--engines/hugo/inventory.cpp87
-rw-r--r--engines/hugo/inventory.h4
-rw-r--r--engines/hugo/module.mk9
-rw-r--r--engines/hugo/mouse.cpp144
-rw-r--r--engines/hugo/mouse.h4
-rw-r--r--engines/hugo/object.cpp577
-rw-r--r--engines/hugo/object.h140
-rw-r--r--engines/hugo/object_v1d.cpp372
-rw-r--r--engines/hugo/object_v1w.cpp385
-rw-r--r--engines/hugo/object_v2d.cpp364
-rw-r--r--engines/hugo/object_v3d.cpp266
-rw-r--r--engines/hugo/parser.cpp127
-rw-r--r--engines/hugo/parser.h18
-rw-r--r--engines/hugo/parser_v1d.cpp193
-rw-r--r--engines/hugo/parser_v1w.cpp231
-rw-r--r--engines/hugo/parser_v2d.cpp51
-rw-r--r--engines/hugo/parser_v3d.cpp78
-rw-r--r--engines/hugo/route.cpp146
-rw-r--r--engines/hugo/route.h6
-rw-r--r--engines/hugo/schedule.cpp1269
-rw-r--r--engines/hugo/schedule.h98
-rw-r--r--engines/hugo/schedule_v1d.cpp301
-rw-r--r--engines/hugo/schedule_v1w.cpp76
-rw-r--r--engines/hugo/schedule_v2d.cpp450
-rw-r--r--engines/hugo/schedule_v3d.cpp13
-rw-r--r--engines/hugo/sound.cpp25
-rw-r--r--engines/hugo/sound.h11
-rw-r--r--engines/hugo/util.cpp77
-rw-r--r--engines/hugo/util.h13
-rw-r--r--engines/kyra/animator_v2.cpp2
-rw-r--r--engines/kyra/debugger.cpp20
-rw-r--r--engines/kyra/debugger.h6
-rw-r--r--engines/kyra/detection.cpp2
-rw-r--r--engines/kyra/detection_tables.h17
-rw-r--r--engines/kyra/gui_hof.cpp58
-rw-r--r--engines/kyra/gui_lok.cpp17
-rw-r--r--engines/kyra/gui_lol.cpp2
-rw-r--r--engines/kyra/gui_mr.cpp24
-rw-r--r--engines/kyra/gui_v2.cpp2
-rw-r--r--engines/kyra/item.h (renamed from engines/sword25/kernel/bs_stdint.h)35
-rw-r--r--engines/kyra/items_hof.cpp34
-rw-r--r--engines/kyra/items_lok.cpp57
-rw-r--r--engines/kyra/items_lol.cpp32
-rw-r--r--engines/kyra/items_mr.cpp46
-rw-r--r--engines/kyra/items_v2.cpp22
-rw-r--r--engines/kyra/kyra_hof.cpp32
-rw-r--r--engines/kyra/kyra_hof.h26
-rw-r--r--engines/kyra/kyra_lok.cpp17
-rw-r--r--engines/kyra/kyra_lok.h24
-rw-r--r--engines/kyra/kyra_mr.cpp30
-rw-r--r--engines/kyra/kyra_mr.h20
-rw-r--r--engines/kyra/kyra_v1.cpp15
-rw-r--r--engines/kyra/kyra_v1.h9
-rw-r--r--engines/kyra/kyra_v2.h26
-rw-r--r--engines/kyra/lol.cpp4
-rw-r--r--engines/kyra/lol.h40
-rw-r--r--engines/kyra/resource_intern.cpp5
-rw-r--r--engines/kyra/saveload.cpp5
-rw-r--r--engines/kyra/saveload_hof.cpp11
-rw-r--r--engines/kyra/saveload_lok.cpp6
-rw-r--r--engines/kyra/saveload_lol.cpp11
-rw-r--r--engines/kyra/saveload_mr.cpp11
-rw-r--r--engines/kyra/scene_hof.cpp12
-rw-r--r--engines/kyra/scene_lok.cpp5
-rw-r--r--engines/kyra/scene_lol.cpp2
-rw-r--r--engines/kyra/scene_mr.cpp22
-rw-r--r--engines/kyra/screen.cpp7
-rw-r--r--engines/kyra/screen.h1
-rw-r--r--engines/kyra/screen_hof.h1
-rw-r--r--engines/kyra/screen_lok.h1
-rw-r--r--engines/kyra/screen_lol.h3
-rw-r--r--engines/kyra/screen_mr.h1
-rw-r--r--engines/kyra/screen_v2.cpp6
-rw-r--r--engines/kyra/script_hof.cpp8
-rw-r--r--engines/kyra/script_lok.cpp5
-rw-r--r--engines/kyra/script_lol.cpp7
-rw-r--r--engines/kyra/script_mr.cpp10
-rw-r--r--engines/kyra/script_tim.cpp2
-rw-r--r--engines/kyra/script_v2.cpp2
-rw-r--r--engines/kyra/sequences_hof.cpp4
-rw-r--r--engines/kyra/sequences_lol.cpp2
-rw-r--r--engines/kyra/sound.h2
-rw-r--r--engines/kyra/sound_adlib.cpp2
-rw-r--r--engines/kyra/sound_adlib.h2
-rw-r--r--engines/kyra/sound_intern.h2
-rw-r--r--engines/kyra/sound_midi.cpp2
-rw-r--r--engines/kyra/sound_towns.cpp6
-rw-r--r--engines/kyra/sprites_lol.cpp9
-rw-r--r--engines/kyra/staticres.cpp27
-rw-r--r--engines/kyra/text_lol.cpp13
-rw-r--r--engines/kyra/text_lol.h2
-rw-r--r--engines/kyra/timer_mr.cpp2
-rw-r--r--engines/lastexpress/data/animation.cpp303
-rw-r--r--engines/lastexpress/data/animation.h124
-rw-r--r--engines/lastexpress/data/archive.cpp116
-rw-r--r--engines/lastexpress/data/archive.h75
-rw-r--r--engines/lastexpress/data/background.cpp143
-rw-r--r--engines/lastexpress/data/background.h83
-rw-r--r--engines/lastexpress/data/cursor.cpp145
-rw-r--r--engines/lastexpress/data/cursor.h95
-rw-r--r--engines/lastexpress/data/font.cpp206
-rw-r--r--engines/lastexpress/data/font.h85
-rw-r--r--engines/lastexpress/data/scene.cpp302
-rw-r--r--engines/lastexpress/data/scene.h249
-rw-r--r--engines/lastexpress/data/sequence.cpp484
-rw-r--r--engines/lastexpress/data/sequence.h208
-rw-r--r--engines/lastexpress/data/snd.cpp142
-rw-r--r--engines/lastexpress/data/snd.h100
-rw-r--r--engines/lastexpress/data/subtitle.cpp245
-rw-r--r--engines/lastexpress/data/subtitle.h82
-rw-r--r--engines/lastexpress/debug.cpp1177
-rw-r--r--engines/lastexpress/debug.h106
-rw-r--r--engines/lastexpress/detection.cpp211
-rw-r--r--engines/lastexpress/drawable.h42
-rw-r--r--engines/lastexpress/entities/abbot.cpp1910
-rw-r--r--engines/lastexpress/entities/abbot.h225
-rw-r--r--engines/lastexpress/entities/alexei.cpp2002
-rw-r--r--engines/lastexpress/entities/alexei.h213
-rw-r--r--engines/lastexpress/entities/alouan.cpp504
-rw-r--r--engines/lastexpress/entities/alouan.h139
-rw-r--r--engines/lastexpress/entities/anna.cpp4032
-rw-r--r--engines/lastexpress/entities/anna.h252
-rw-r--r--engines/lastexpress/entities/august.cpp3536
-rw-r--r--engines/lastexpress/entities/august.h275
-rw-r--r--engines/lastexpress/entities/boutarel.cpp1260
-rw-r--r--engines/lastexpress/entities/boutarel.h188
-rw-r--r--engines/lastexpress/entities/chapters.cpp1820
-rw-r--r--engines/lastexpress/entities/chapters.h166
-rw-r--r--engines/lastexpress/entities/cooks.cpp571
-rw-r--r--engines/lastexpress/entities/cooks.h109
-rw-r--r--engines/lastexpress/entities/coudert.cpp4179
-rw-r--r--engines/lastexpress/entities/coudert.h229
-rw-r--r--engines/lastexpress/entities/entity.cpp499
-rw-r--r--engines/lastexpress/entities/entity.h801
-rw-r--r--engines/lastexpress/entities/entity39.cpp103
-rw-r--r--engines/lastexpress/entities/entity39.h78
-rw-r--r--engines/lastexpress/entities/entity_intern.h528
-rw-r--r--engines/lastexpress/entities/francois.cpp1295
-rw-r--r--engines/lastexpress/entities/francois.h170
-rw-r--r--engines/lastexpress/entities/gendarmes.cpp620
-rw-r--r--engines/lastexpress/entities/gendarmes.h99
-rw-r--r--engines/lastexpress/entities/hadija.cpp532
-rw-r--r--engines/lastexpress/entities/hadija.h144
-rw-r--r--engines/lastexpress/entities/ivo.cpp829
-rw-r--r--engines/lastexpress/entities/ivo.h177
-rw-r--r--engines/lastexpress/entities/kahina.cpp1528
-rw-r--r--engines/lastexpress/entities/kahina.h166
-rw-r--r--engines/lastexpress/entities/kronos.cpp892
-rw-r--r--engines/lastexpress/entities/kronos.h138
-rw-r--r--engines/lastexpress/entities/mahmud.cpp839
-rw-r--r--engines/lastexpress/entities/mahmud.h153
-rw-r--r--engines/lastexpress/entities/max.cpp628
-rw-r--r--engines/lastexpress/entities/max.h129
-rw-r--r--engines/lastexpress/entities/mertens.cpp4113
-rw-r--r--engines/lastexpress/entities/mertens.h220
-rw-r--r--engines/lastexpress/entities/milos.cpp1805
-rw-r--r--engines/lastexpress/entities/milos.h175
-rw-r--r--engines/lastexpress/entities/mmeboutarel.cpp1301
-rw-r--r--engines/lastexpress/entities/mmeboutarel.h164
-rw-r--r--engines/lastexpress/entities/pascale.cpp1232
-rw-r--r--engines/lastexpress/entities/pascale.h165
-rw-r--r--engines/lastexpress/entities/rebecca.cpp1838
-rw-r--r--engines/lastexpress/entities/rebecca.h230
-rw-r--r--engines/lastexpress/entities/salko.cpp642
-rw-r--r--engines/lastexpress/entities/salko.h149
-rw-r--r--engines/lastexpress/entities/servers0.cpp1039
-rw-r--r--engines/lastexpress/entities/servers0.h172
-rw-r--r--engines/lastexpress/entities/servers1.cpp787
-rw-r--r--engines/lastexpress/entities/servers1.h167
-rw-r--r--engines/lastexpress/entities/sophie.cpp279
-rw-r--r--engines/lastexpress/entities/sophie.h98
-rw-r--r--engines/lastexpress/entities/tables.cpp221
-rw-r--r--engines/lastexpress/entities/tables.h77
-rw-r--r--engines/lastexpress/entities/tatiana.cpp2272
-rw-r--r--engines/lastexpress/entities/tatiana.h235
-rw-r--r--engines/lastexpress/entities/train.cpp575
-rw-r--r--engines/lastexpress/entities/train.h95
-rw-r--r--engines/lastexpress/entities/vassili.cpp589
-rw-r--r--engines/lastexpress/entities/vassili.h110
-rw-r--r--engines/lastexpress/entities/verges.cpp1898
-rw-r--r--engines/lastexpress/entities/verges.h182
-rw-r--r--engines/lastexpress/entities/vesna.cpp1161
-rw-r--r--engines/lastexpress/entities/vesna.h176
-rw-r--r--engines/lastexpress/entities/yasmin.cpp490
-rw-r--r--engines/lastexpress/entities/yasmin.h142
-rw-r--r--engines/lastexpress/eventhandler.h53
-rw-r--r--engines/lastexpress/game/action.cpp1968
-rw-r--r--engines/lastexpress/game/action.h135
-rw-r--r--engines/lastexpress/game/beetle.cpp517
-rw-r--r--engines/lastexpress/game/beetle.h121
-rw-r--r--engines/lastexpress/game/entities.cpp2748
-rw-r--r--engines/lastexpress/game/entities.h380
-rw-r--r--engines/lastexpress/game/fight.cpp1586
-rw-r--r--engines/lastexpress/game/fight.h269
-rw-r--r--engines/lastexpress/game/inventory.cpp599
-rw-r--r--engines/lastexpress/game/inventory.h180
-rw-r--r--engines/lastexpress/game/logic.cpp593
-rw-r--r--engines/lastexpress/game/logic.h92
-rw-r--r--engines/lastexpress/game/menu.cpp1544
-rw-r--r--engines/lastexpress/game/menu.h210
-rw-r--r--engines/lastexpress/game/object.cpp109
-rw-r--r--engines/lastexpress/game/object.h92
-rw-r--r--engines/lastexpress/game/savegame.cpp569
-rw-r--r--engines/lastexpress/game/savegame.h289
-rw-r--r--engines/lastexpress/game/savepoint.cpp296
-rw-r--r--engines/lastexpress/game/savepoint.h151
-rw-r--r--engines/lastexpress/game/scenes.cpp1198
-rw-r--r--engines/lastexpress/game/scenes.h120
-rw-r--r--engines/lastexpress/game/sound.cpp1771
-rw-r--r--engines/lastexpress/game/sound.h355
-rw-r--r--engines/lastexpress/game/state.cpp82
-rw-r--r--engines/lastexpress/game/state.h657
-rw-r--r--engines/lastexpress/graphics.cpp166
-rw-r--r--engines/lastexpress/graphics.h76
-rw-r--r--engines/lastexpress/helpers.h102
-rw-r--r--engines/lastexpress/lastexpress.cpp314
-rw-r--r--engines/lastexpress/lastexpress.h153
-rw-r--r--engines/lastexpress/module.mk73
-rw-r--r--engines/lastexpress/resource.cpp249
-rw-r--r--engines/lastexpress/resource.h72
-rw-r--r--engines/lastexpress/shared.h1716
-rw-r--r--engines/lure/debugger.cpp9
-rw-r--r--engines/lure/hotspots.cpp44
-rw-r--r--engines/lure/hotspots.h3
-rw-r--r--engines/lure/lure.h6
-rw-r--r--engines/lure/menu.cpp2
-rw-r--r--engines/lure/res.cpp2
-rw-r--r--engines/lure/res_struct.cpp67
-rw-r--r--engines/lure/res_struct.h11
-rw-r--r--engines/lure/sound.cpp2
-rw-r--r--engines/m4/assets.cpp34
-rw-r--r--engines/m4/compression.cpp6
-rw-r--r--engines/m4/compression.h4
-rw-r--r--engines/m4/console.cpp16
-rw-r--r--engines/m4/converse.cpp316
-rw-r--r--engines/m4/converse.h2
-rw-r--r--engines/m4/dialogs.cpp2
-rw-r--r--engines/m4/events.cpp2
-rw-r--r--engines/m4/font.cpp10
-rw-r--r--engines/m4/globals.cpp33
-rw-r--r--engines/m4/globals.h11
-rw-r--r--engines/m4/graphics.cpp14
-rw-r--r--engines/m4/hotspot.cpp2
-rw-r--r--engines/m4/m4.cpp33
-rw-r--r--engines/m4/m4.h7
-rw-r--r--engines/m4/m4_scene.cpp8
-rw-r--r--engines/m4/mads_anim.cpp8
-rw-r--r--engines/m4/mads_logic.cpp8
-rw-r--r--engines/m4/mads_menus.cpp2
-rw-r--r--engines/m4/mads_scene.cpp17
-rw-r--r--engines/m4/mads_scene.h4
-rw-r--r--engines/m4/midi.cpp2
-rw-r--r--engines/m4/rails.cpp4
-rw-r--r--engines/m4/resource.cpp28
-rw-r--r--engines/m4/script.cpp199
-rw-r--r--engines/m4/script.h4
-rw-r--r--engines/m4/sound.cpp16
-rw-r--r--engines/m4/woodscript.cpp12
-rw-r--r--engines/m4/woodscript.h2
-rw-r--r--engines/m4/ws_machine.cpp62
-rw-r--r--engines/m4/ws_sequence.cpp101
-rw-r--r--engines/made/console.cpp43
-rw-r--r--engines/made/console.h50
-rw-r--r--engines/made/database.cpp4
-rw-r--r--engines/made/made.cpp11
-rw-r--r--engines/made/made.h11
-rw-r--r--engines/made/module.mk1
-rw-r--r--engines/made/redreader.cpp8
-rw-r--r--engines/made/redreader.h4
-rw-r--r--engines/made/resource.cpp2
-rw-r--r--engines/made/screen.cpp2
-rw-r--r--engines/made/scriptfuncs.cpp2
-rw-r--r--engines/metaengine.h1
-rw-r--r--engines/mohawk/bitmap.cpp218
-rw-r--r--engines/mohawk/bitmap.h72
-rw-r--r--engines/mohawk/console.cpp36
-rw-r--r--engines/mohawk/cursors.cpp195
-rw-r--r--engines/mohawk/cursors.h97
-rw-r--r--engines/mohawk/detection.cpp6
-rw-r--r--engines/mohawk/detection_tables.h67
-rw-r--r--engines/mohawk/dialogs.cpp8
-rw-r--r--engines/mohawk/dialogs.h8
-rw-r--r--engines/mohawk/graphics.cpp434
-rw-r--r--engines/mohawk/graphics.h115
-rw-r--r--engines/mohawk/livingbooks.cpp45
-rw-r--r--engines/mohawk/livingbooks.h15
-rw-r--r--engines/mohawk/module.mk1
-rw-r--r--engines/mohawk/mohawk.cpp32
-rw-r--r--engines/mohawk/mohawk.h18
-rw-r--r--engines/mohawk/myst.cpp268
-rw-r--r--engines/mohawk/myst.h24
-rw-r--r--engines/mohawk/myst_saveload.cpp15
-rw-r--r--engines/mohawk/myst_saveload.h6
-rw-r--r--engines/mohawk/myst_scripts.cpp137
-rw-r--r--engines/mohawk/myst_scripts.h28
-rw-r--r--engines/mohawk/resource.cpp165
-rw-r--r--engines/mohawk/resource.h95
-rw-r--r--engines/mohawk/resource_cache.cpp21
-rw-r--r--engines/mohawk/resource_cache.h2
-rw-r--r--engines/mohawk/riven.cpp40
-rw-r--r--engines/mohawk/riven_external.cpp126
-rw-r--r--engines/mohawk/riven_external.h8
-rw-r--r--engines/mohawk/riven_saveload.cpp15
-rw-r--r--engines/mohawk/riven_saveload.h1
-rw-r--r--engines/mohawk/riven_scripts.cpp60
-rw-r--r--engines/mohawk/riven_scripts.h4
-rw-r--r--engines/mohawk/sound.cpp50
-rw-r--r--engines/mohawk/sound.h3
-rw-r--r--engines/mohawk/video.cpp12
-rw-r--r--engines/mohawk/video.h8
-rw-r--r--engines/parallaction/detection.cpp2
-rw-r--r--engines/parallaction/disk_ns.cpp4
-rw-r--r--engines/parallaction/font.cpp10
-rw-r--r--engines/parallaction/gfxbase.cpp2
-rw-r--r--engines/parallaction/gui_ns.cpp2
-rw-r--r--engines/parallaction/parallaction.cpp2
-rw-r--r--engines/parallaction/parallaction.h7
-rw-r--r--engines/parallaction/parallaction_br.cpp3
-rw-r--r--engines/parallaction/parser.cpp3
-rw-r--r--engines/parallaction/parser.h9
-rw-r--r--engines/parallaction/parser_ns.cpp3
-rw-r--r--engines/parallaction/saveload.cpp2
-rw-r--r--engines/parallaction/staticres.cpp399
-rw-r--r--engines/queen/debug.cpp8
-rw-r--r--engines/queen/logic.cpp6
-rw-r--r--engines/queen/midiadlib.cpp2
-rw-r--r--engines/queen/queen.h4
-rw-r--r--engines/queen/resource.cpp1
-rw-r--r--engines/queen/sound.cpp8
-rw-r--r--engines/queen/talk.cpp2
-rw-r--r--engines/saga/actor.cpp388
-rw-r--r--engines/saga/actor.h72
-rw-r--r--engines/saga/actor_walk.cpp42
-rw-r--r--engines/saga/animation.cpp128
-rw-r--r--engines/saga/animation.h33
-rw-r--r--engines/saga/detection.cpp6
-rw-r--r--engines/saga/events.cpp127
-rw-r--r--engines/saga/events.h26
-rw-r--r--engines/saga/font.cpp74
-rw-r--r--engines/saga/font.h11
-rw-r--r--engines/saga/gfx.cpp33
-rw-r--r--engines/saga/gfx.h7
-rw-r--r--engines/saga/image.cpp112
-rw-r--r--engines/saga/interface.cpp152
-rw-r--r--engines/saga/interface.h11
-rw-r--r--engines/saga/introproc_ihnm.cpp20
-rw-r--r--engines/saga/introproc_ite.cpp146
-rw-r--r--engines/saga/isomap.cpp154
-rw-r--r--engines/saga/isomap.h21
-rw-r--r--engines/saga/itedata.cpp2
-rw-r--r--engines/saga/itedata.h8
-rw-r--r--engines/saga/music.cpp40
-rw-r--r--engines/saga/music.h8
-rw-r--r--engines/saga/objectmap.cpp173
-rw-r--r--engines/saga/objectmap.h38
-rw-r--r--engines/saga/palanim.cpp147
-rw-r--r--engines/saga/palanim.h19
-rw-r--r--engines/saga/puzzle.cpp4
-rw-r--r--engines/saga/resource.cpp35
-rw-r--r--engines/saga/resource.h2
-rw-r--r--engines/saga/resource_res.cpp131
-rw-r--r--engines/saga/saga.cpp64
-rw-r--r--engines/saga/saga.h169
-rw-r--r--engines/saga/saveload.cpp11
-rw-r--r--engines/saga/scene.cpp383
-rw-r--r--engines/saga/scene.h81
-rw-r--r--engines/saga/script.cpp154
-rw-r--r--engines/saga/script.h94
-rw-r--r--engines/saga/sfuncs.cpp40
-rw-r--r--engines/saga/sfuncs_ihnm.cpp10
-rw-r--r--engines/saga/shorten.cpp23
-rw-r--r--engines/saga/sndres.cpp51
-rw-r--r--engines/saga/sndres.h11
-rw-r--r--engines/saga/sound.cpp3
-rw-r--r--engines/saga/sound.h1
-rw-r--r--engines/saga/sprite.cpp250
-rw-r--r--engines/saga/sprite.h38
-rw-r--r--engines/saga/sthread.cpp28
-rw-r--r--engines/savestate.cpp6
-rw-r--r--engines/savestate.h5
-rw-r--r--engines/sci/console.cpp378
-rw-r--r--engines/sci/console.h7
-rw-r--r--engines/sci/detection.cpp34
-rw-r--r--engines/sci/detection_tables.h204
-rw-r--r--engines/sci/engine/features.cpp90
-rw-r--r--engines/sci/engine/features.h24
-rw-r--r--engines/sci/engine/kernel.cpp49
-rw-r--r--engines/sci/engine/kernel.h4
-rw-r--r--engines/sci/engine/kernel_tables.h870
-rw-r--r--engines/sci/engine/kfile.cpp18
-rw-r--r--engines/sci/engine/kgraphics.cpp37
-rw-r--r--engines/sci/engine/klists.cpp68
-rw-r--r--engines/sci/engine/kmath.cpp4
-rw-r--r--engines/sci/engine/kmisc.cpp53
-rw-r--r--engines/sci/engine/kparse.cpp4
-rw-r--r--engines/sci/engine/kpathing.cpp25
-rw-r--r--engines/sci/engine/kscripts.cpp28
-rw-r--r--engines/sci/engine/ksound.cpp2
-rw-r--r--engines/sci/engine/kstring.cpp28
-rw-r--r--engines/sci/engine/kvideo.cpp16
-rw-r--r--engines/sci/engine/message.cpp2
-rw-r--r--engines/sci/engine/object.cpp269
-rw-r--r--engines/sci/engine/object.h250
-rw-r--r--engines/sci/engine/savegame.cpp274
-rw-r--r--engines/sci/engine/savegame.h17
-rw-r--r--engines/sci/engine/script.cpp299
-rw-r--r--engines/sci/engine/script.h57
-rw-r--r--engines/sci/engine/script_patches.cpp275
-rw-r--r--engines/sci/engine/scriptdebug.cpp155
-rw-r--r--engines/sci/engine/seg_manager.cpp4
-rw-r--r--engines/sci/engine/seg_manager.h5
-rw-r--r--engines/sci/engine/segment.cpp139
-rw-r--r--engines/sci/engine/segment.h134
-rw-r--r--engines/sci/engine/selector.cpp4
-rw-r--r--engines/sci/engine/selector.h13
-rw-r--r--engines/sci/engine/state.cpp3
-rw-r--r--engines/sci/engine/state.h1
-rw-r--r--engines/sci/engine/static_selectors.cpp33
-rw-r--r--engines/sci/engine/vm.cpp149
-rw-r--r--engines/sci/engine/workarounds.cpp33
-rw-r--r--engines/sci/engine/workarounds.h1
-rw-r--r--engines/sci/event.cpp167
-rw-r--r--engines/sci/graphics/animate.cpp34
-rw-r--r--engines/sci/graphics/animate.h8
-rw-r--r--engines/sci/graphics/cursor.cpp20
-rw-r--r--engines/sci/graphics/cursor.h7
-rw-r--r--engines/sci/graphics/font.cpp30
-rw-r--r--engines/sci/graphics/font.h4
-rw-r--r--engines/sci/graphics/frameout.cpp117
-rw-r--r--engines/sci/graphics/frameout.h2
-rw-r--r--engines/sci/graphics/helpers.h36
-rw-r--r--engines/sci/graphics/maciconbar.cpp4
-rw-r--r--engines/sci/graphics/menu.cpp2
-rw-r--r--engines/sci/graphics/palette.cpp17
-rw-r--r--engines/sci/graphics/picture.cpp13
-rw-r--r--engines/sci/graphics/ports.cpp9
-rw-r--r--engines/sci/graphics/ports.h14
-rw-r--r--engines/sci/graphics/screen.cpp52
-rw-r--r--engines/sci/graphics/screen.h3
-rw-r--r--engines/sci/graphics/view.cpp35
-rw-r--r--engines/sci/graphics/view.h1
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/parser/grammar.cpp40
-rw-r--r--engines/sci/parser/said.cpp2
-rw-r--r--engines/sci/parser/vocabulary.cpp68
-rw-r--r--engines/sci/parser/vocabulary.h2
-rw-r--r--engines/sci/resource.cpp247
-rw-r--r--engines/sci/resource.h15
-rw-r--r--engines/sci/resource_audio.cpp2
-rw-r--r--engines/sci/sci.cpp136
-rw-r--r--engines/sci/sci.h13
-rw-r--r--engines/sci/sound/audio.cpp11
-rw-r--r--engines/sci/sound/drivers/amigamac.cpp19
-rw-r--r--engines/sci/sound/drivers/gm_names.h220
-rw-r--r--engines/sci/sound/drivers/map-mt32-to-gm.h447
-rw-r--r--engines/sci/sound/drivers/midi.cpp130
-rw-r--r--engines/sci/sound/drivers/mididriver.h10
-rw-r--r--engines/sci/sound/midiparser_sci.cpp44
-rw-r--r--engines/sci/sound/midiparser_sci.h1
-rw-r--r--engines/sci/sound/music.cpp51
-rw-r--r--engines/sci/sound/music.h7
-rw-r--r--engines/sci/sound/soundcmd.cpp31
-rw-r--r--engines/sci/sound/soundcmd.h4
-rw-r--r--engines/sci/video/seq_decoder.cpp1
-rw-r--r--engines/scumm/actor.cpp74
-rw-r--r--engines/scumm/actor.h15
-rw-r--r--engines/scumm/akos.cpp8
-rw-r--r--engines/scumm/boxes.cpp28
-rw-r--r--engines/scumm/charset-fontdata.cpp10
-rw-r--r--engines/scumm/charset.cpp462
-rw-r--r--engines/scumm/charset.h29
-rw-r--r--engines/scumm/costume.cpp2
-rw-r--r--engines/scumm/debugger.cpp5
-rw-r--r--engines/scumm/detection.cpp22
-rw-r--r--engines/scumm/detection_tables.h2
-rw-r--r--engines/scumm/dialogs.cpp21
-rw-r--r--engines/scumm/file.cpp26
-rw-r--r--engines/scumm/file.h14
-rw-r--r--engines/scumm/file_nes.cpp43
-rw-r--r--engines/scumm/file_nes.h7
-rw-r--r--engines/scumm/gfx.cpp22
-rw-r--r--engines/scumm/he/animation_he.h2
-rw-r--r--engines/scumm/he/cup_player_he.cpp1
-rw-r--r--engines/scumm/he/logic_he.cpp32
-rw-r--r--engines/scumm/he/logic_he.h8
-rw-r--r--engines/scumm/he/palette_he.cpp4
-rw-r--r--engines/scumm/he/resource_he.cpp297
-rw-r--r--engines/scumm/he/resource_he.h5
-rw-r--r--engines/scumm/he/script_v100he.cpp7
-rw-r--r--engines/scumm/he/script_v60he.cpp7
-rw-r--r--engines/scumm/he/script_v70he.cpp1
-rw-r--r--engines/scumm/he/script_v72he.cpp16
-rw-r--r--engines/scumm/he/script_v80he.cpp11
-rw-r--r--engines/scumm/he/script_v90he.cpp55
-rw-r--r--engines/scumm/he/sound_he.cpp7
-rw-r--r--engines/scumm/he/wiz_he.cpp11
-rw-r--r--engines/scumm/he/wiz_he.h3
-rw-r--r--engines/scumm/help.cpp347
-rw-r--r--engines/scumm/imuse/imuse.cpp4
-rw-r--r--engines/scumm/imuse_digi/dimuse_bndmgr.cpp2
-rw-r--r--engines/scumm/imuse_digi/dimuse_bndmgr.h2
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.cpp8
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.h2
-rw-r--r--engines/scumm/input.cpp2
-rw-r--r--engines/scumm/insane/insane.cpp6
-rw-r--r--engines/scumm/insane/insane_ben.cpp2
-rw-r--r--engines/scumm/insane/insane_enemy.cpp4
-rw-r--r--engines/scumm/module.mk1
-rw-r--r--engines/scumm/object.cpp10
-rw-r--r--engines/scumm/palette.cpp8
-rw-r--r--engines/scumm/player_nes.h2
-rw-r--r--engines/scumm/player_pce.cpp324
-rw-r--r--engines/scumm/player_v2.cpp660
-rw-r--r--engines/scumm/player_v2.h264
-rw-r--r--engines/scumm/player_v2base.cpp657
-rw-r--r--engines/scumm/player_v2base.h148
-rw-r--r--engines/scumm/player_v2cms.cpp761
-rw-r--r--engines/scumm/player_v2cms.h166
-rw-r--r--engines/scumm/resource.cpp13
-rw-r--r--engines/scumm/resource_v2.cpp12
-rw-r--r--engines/scumm/saveload.cpp9
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script.cpp76
-rw-r--r--engines/scumm/script_v0.cpp7
-rw-r--r--engines/scumm/script_v2.cpp3
-rw-r--r--engines/scumm/script_v5.cpp18
-rw-r--r--engines/scumm/script_v6.cpp15
-rw-r--r--engines/scumm/script_v8.cpp2
-rw-r--r--engines/scumm/scumm-md5.h6
-rw-r--r--engines/scumm/scumm.cpp30
-rw-r--r--engines/scumm/scumm.h25
-rw-r--r--engines/scumm/smush/codec47.cpp4
-rw-r--r--engines/scumm/sound.cpp8
-rw-r--r--engines/scumm/string.cpp22
-rw-r--r--engines/scumm/verbs.cpp34
-rw-r--r--engines/sky/disk.cpp2
-rw-r--r--engines/sky/logic.cpp2
-rw-r--r--engines/sky/sky.h4
-rw-r--r--engines/sword1/console.cpp43
-rw-r--r--engines/sword1/console.h50
-rw-r--r--engines/sword1/control.cpp8
-rw-r--r--engines/sword1/detection.cpp8
-rw-r--r--engines/sword1/module.mk1
-rw-r--r--engines/sword1/resman.cpp13
-rw-r--r--engines/sword1/screen.cpp4
-rw-r--r--engines/sword1/sound.cpp9
-rw-r--r--engines/sword1/sword1.cpp14
-rw-r--r--engines/sword1/sword1.h10
-rw-r--r--engines/sword2/animation.cpp6
-rw-r--r--engines/sword2/animation.h1
-rw-r--r--engines/sword2/console.cpp7
-rw-r--r--engines/sword2/controls.cpp6
-rw-r--r--engines/sword2/debug.cpp9
-rw-r--r--engines/sword2/header.cpp364
-rw-r--r--engines/sword2/header.h255
-rw-r--r--engines/sword2/icons.cpp2
-rw-r--r--engines/sword2/logic.cpp10
-rw-r--r--engines/sword2/logic.h2
-rw-r--r--engines/sword2/module.mk1
-rw-r--r--engines/sword2/mouse.cpp1
-rw-r--r--engines/sword2/music.cpp15
-rw-r--r--engines/sword2/object.h73
-rw-r--r--engines/sword2/palette.cpp3
-rw-r--r--engines/sword2/protocol.cpp4
-rw-r--r--engines/sword2/render.cpp4
-rw-r--r--engines/sword2/resman.cpp9
-rw-r--r--engines/sword2/router.cpp2
-rw-r--r--engines/sword2/saveload.cpp1
-rw-r--r--engines/sword2/screen.h15
-rw-r--r--engines/sword2/sound.cpp13
-rw-r--r--engines/sword2/sound.h3
-rw-r--r--engines/sword2/sword2.cpp80
-rw-r--r--engines/sword2/sword2.h13
-rw-r--r--engines/sword25/console.cpp43
-rw-r--r--engines/sword25/console.h (renamed from engines/sword25/gfx/image/imageloader_ids.h)44
-rw-r--r--engines/sword25/detection.cpp34
-rw-r--r--engines/sword25/fmv/movieplayer.cpp112
-rw-r--r--engines/sword25/fmv/movieplayer.h13
-rw-r--r--engines/sword25/fmv/movieplayer_script.cpp26
-rw-r--r--engines/sword25/fmv/theora_decoder.cpp29
-rw-r--r--engines/sword25/fmv/theora_decoder.h6
-rw-r--r--engines/sword25/fmv/yuvtorgba.cpp4
-rw-r--r--engines/sword25/fmv/yuvtorgba.h5
-rw-r--r--engines/sword25/gfx/animation.cpp185
-rw-r--r--engines/sword25/gfx/animation.h16
-rw-r--r--engines/sword25/gfx/animationdescription.h2
-rw-r--r--engines/sword25/gfx/animationresource.cpp10
-rw-r--r--engines/sword25/gfx/animationtemplate.cpp30
-rw-r--r--engines/sword25/gfx/animationtemplateregistry.cpp4
-rw-r--r--engines/sword25/gfx/animationtemplateregistry.h14
-rw-r--r--engines/sword25/gfx/bitmap.cpp40
-rw-r--r--engines/sword25/gfx/bitmap.h8
-rw-r--r--engines/sword25/gfx/bitmapresource.cpp6
-rw-r--r--engines/sword25/gfx/bitmapresource.h5
-rw-r--r--engines/sword25/gfx/dynamicbitmap.cpp40
-rw-r--r--engines/sword25/gfx/dynamicbitmap.h25
-rw-r--r--engines/sword25/gfx/fontresource.cpp65
-rw-r--r--engines/sword25/gfx/fontresource.h54
-rw-r--r--engines/sword25/gfx/framecounter.cpp28
-rw-r--r--engines/sword25/gfx/framecounter.h29
-rw-r--r--engines/sword25/gfx/graphicengine.cpp395
-rw-r--r--engines/sword25/gfx/graphicengine.h218
-rw-r--r--engines/sword25/gfx/graphicengine_script.cpp1454
-rw-r--r--engines/sword25/gfx/image/art.cpp71
-rw-r--r--engines/sword25/gfx/image/b25sloader.cpp119
-rw-r--r--engines/sword25/gfx/image/b25sloader.h67
-rw-r--r--engines/sword25/gfx/image/image.h2
-rw-r--r--engines/sword25/gfx/image/imageloader.cpp129
-rw-r--r--engines/sword25/gfx/image/imageloader.h209
-rw-r--r--engines/sword25/gfx/image/pngloader.cpp233
-rw-r--r--engines/sword25/gfx/image/pngloader.h76
-rw-r--r--engines/sword25/gfx/image/renderedimage.cpp18
-rw-r--r--engines/sword25/gfx/image/swimage.cpp30
-rw-r--r--engines/sword25/gfx/image/swimage.h8
-rw-r--r--engines/sword25/gfx/image/vectorimage.cpp1
-rw-r--r--engines/sword25/gfx/image/vectorimagerenderer.cpp18
-rw-r--r--engines/sword25/gfx/panel.cpp26
-rw-r--r--engines/sword25/gfx/panel.h12
-rw-r--r--engines/sword25/gfx/renderobject.cpp53
-rw-r--r--engines/sword25/gfx/renderobject.h17
-rw-r--r--engines/sword25/gfx/renderobjectmanager.cpp6
-rw-r--r--engines/sword25/gfx/renderobjectmanager.h2
-rw-r--r--engines/sword25/gfx/renderobjectptr.h6
-rw-r--r--engines/sword25/gfx/renderobjectregistry.cpp4
-rw-r--r--engines/sword25/gfx/renderobjectregistry.h29
-rw-r--r--engines/sword25/gfx/screenshot.cpp67
-rw-r--r--engines/sword25/gfx/screenshot.h12
-rw-r--r--engines/sword25/gfx/staticbitmap.cpp66
-rw-r--r--engines/sword25/gfx/staticbitmap.h24
-rw-r--r--engines/sword25/gfx/text.cpp328
-rw-r--r--engines/sword25/gfx/text.h74
-rw-r--r--engines/sword25/gfx/timedrenderobject.h8
-rw-r--r--engines/sword25/input/inputengine.cpp376
-rw-r--r--engines/sword25/input/inputengine.h291
-rw-r--r--engines/sword25/input/inputengine_script.cpp266
-rw-r--r--engines/sword25/kernel/callbackregistry.cpp131
-rw-r--r--engines/sword25/kernel/callbackregistry.h93
-rw-r--r--engines/sword25/kernel/filesystemutil.cpp119
-rw-r--r--engines/sword25/kernel/filesystemutil.h33
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.cpp144
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.h42
-rw-r--r--engines/sword25/kernel/kernel.cpp403
-rw-r--r--engines/sword25/kernel/kernel.h242
-rw-r--r--engines/sword25/kernel/kernel_script.cpp645
-rw-r--r--engines/sword25/kernel/log.cpp178
-rw-r--r--engines/sword25/kernel/log.h93
-rw-r--r--engines/sword25/kernel/objectregistry.h4
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.cpp96
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.h34
-rw-r--r--engines/sword25/kernel/persistable.h2
-rw-r--r--engines/sword25/kernel/persistenceblock.h62
-rw-r--r--engines/sword25/kernel/persistenceservice.cpp421
-rw-r--r--engines/sword25/kernel/persistenceservice.h32
-rw-r--r--engines/sword25/kernel/resmanager.cpp199
-rw-r--r--engines/sword25/kernel/resmanager.h80
-rw-r--r--engines/sword25/kernel/resource.cpp7
-rw-r--r--engines/sword25/kernel/resource.h22
-rw-r--r--engines/sword25/kernel/resservice.h5
-rw-r--r--engines/sword25/kernel/scummvmwindow.cpp297
-rw-r--r--engines/sword25/kernel/scummvmwindow.h85
-rw-r--r--engines/sword25/kernel/service.h4
-rw-r--r--engines/sword25/kernel/service_ids.h80
-rw-r--r--engines/sword25/kernel/string.h111
-rw-r--r--engines/sword25/kernel/window.cpp69
-rw-r--r--engines/sword25/kernel/window.h177
-rw-r--r--engines/sword25/math/geometry.cpp4
-rw-r--r--engines/sword25/math/geometry_script.cpp28
-rw-r--r--engines/sword25/math/polygon.cpp2
-rw-r--r--engines/sword25/math/polygon.h5
-rw-r--r--engines/sword25/math/region.cpp10
-rw-r--r--engines/sword25/math/regionregistry.cpp4
-rw-r--r--engines/sword25/math/regionregistry.h15
-rw-r--r--engines/sword25/math/vertex.cpp7
-rw-r--r--engines/sword25/math/vertex.h29
-rw-r--r--engines/sword25/math/walkregion.cpp15
-rw-r--r--engines/sword25/module.mk16
-rw-r--r--engines/sword25/package/packagemanager.cpp37
-rw-r--r--engines/sword25/package/packagemanager.h17
-rw-r--r--engines/sword25/package/packagemanager_script.cpp20
-rw-r--r--engines/sword25/script/lua_extensions.cpp2
-rw-r--r--engines/sword25/script/luabindhelper.h9
-rw-r--r--engines/sword25/script/luacallback.cpp7
-rw-r--r--engines/sword25/script/luacallback.h6
-rw-r--r--engines/sword25/script/luascript.cpp18
-rw-r--r--engines/sword25/script/luascript.h9
-rw-r--r--engines/sword25/script/script.h4
-rw-r--r--engines/sword25/sfx/soundengine.cpp6
-rw-r--r--engines/sword25/sfx/soundengine.h2
-rw-r--r--engines/sword25/sfx/soundengine_script.cpp80
-rw-r--r--engines/sword25/sword25.cpp67
-rw-r--r--engines/sword25/sword25.h21
-rw-r--r--engines/sword25/util/lua/lapi.cpp (renamed from engines/sword25/util/lua/lapi.c)14
-rw-r--r--engines/sword25/util/lua/lapi.h2
-rw-r--r--engines/sword25/util/lua/lauxlib.cpp (renamed from engines/sword25/util/lua/lauxlib.c)2
-rw-r--r--engines/sword25/util/lua/lauxlib.h2
-rw-r--r--engines/sword25/util/lua/lbaselib.cpp (renamed from engines/sword25/util/lua/lbaselib.c)2
-rw-r--r--engines/sword25/util/lua/lcode.cpp (renamed from engines/sword25/util/lua/lcode.c)2
-rw-r--r--engines/sword25/util/lua/lcode.h2
-rw-r--r--engines/sword25/util/lua/ldblib.cpp (renamed from engines/sword25/util/lua/ldblib.c)2
-rw-r--r--engines/sword25/util/lua/ldebug.cpp (renamed from engines/sword25/util/lua/ldebug.c)2
-rw-r--r--engines/sword25/util/lua/ldebug.h2
-rw-r--r--engines/sword25/util/lua/ldo.cpp (renamed from engines/sword25/util/lua/ldo.c)2
-rw-r--r--engines/sword25/util/lua/ldo.h2
-rw-r--r--engines/sword25/util/lua/ldump.cpp (renamed from engines/sword25/util/lua/ldump.c)2
-rw-r--r--engines/sword25/util/lua/lfunc.cpp (renamed from engines/sword25/util/lua/lfunc.c)2
-rw-r--r--engines/sword25/util/lua/lfunc.h2
-rw-r--r--engines/sword25/util/lua/lgc.cpp (renamed from engines/sword25/util/lua/lgc.c)2
-rw-r--r--engines/sword25/util/lua/lgc.h2
-rw-r--r--engines/sword25/util/lua/linit.cpp (renamed from engines/sword25/util/lua/linit.c)2
-rw-r--r--engines/sword25/util/lua/liolib.cpp (renamed from engines/sword25/util/lua/liolib.c)2
-rw-r--r--engines/sword25/util/lua/llex.cpp (renamed from engines/sword25/util/lua/llex.c)2
-rw-r--r--engines/sword25/util/lua/llex.h2
-rw-r--r--engines/sword25/util/lua/llimits.h2
-rw-r--r--engines/sword25/util/lua/lmathlib.cpp (renamed from engines/sword25/util/lua/lmathlib.c)12
-rw-r--r--engines/sword25/util/lua/lmem.cpp (renamed from engines/sword25/util/lua/lmem.c)2
-rw-r--r--engines/sword25/util/lua/lmem.h2
-rw-r--r--engines/sword25/util/lua/loadlib.cpp (renamed from engines/sword25/util/lua/loadlib.c)2
-rw-r--r--engines/sword25/util/lua/lobject.cpp (renamed from engines/sword25/util/lua/lobject.c)2
-rw-r--r--engines/sword25/util/lua/lobject.h2
-rw-r--r--engines/sword25/util/lua/lopcodes.cpp (renamed from engines/sword25/util/lua/lopcodes.c)2
-rw-r--r--engines/sword25/util/lua/lopcodes.h2
-rw-r--r--engines/sword25/util/lua/loslib.cpp (renamed from engines/sword25/util/lua/loslib.c)2
-rw-r--r--engines/sword25/util/lua/lparser.cpp (renamed from engines/sword25/util/lua/lparser.c)2
-rw-r--r--engines/sword25/util/lua/lparser.h2
-rw-r--r--engines/sword25/util/lua/lstate.cpp (renamed from engines/sword25/util/lua/lstate.c)2
-rw-r--r--engines/sword25/util/lua/lstate.h2
-rw-r--r--engines/sword25/util/lua/lstring.cpp (renamed from engines/sword25/util/lua/lstring.c)2
-rw-r--r--engines/sword25/util/lua/lstring.h2
-rw-r--r--engines/sword25/util/lua/lstrlib.cpp (renamed from engines/sword25/util/lua/lstrlib.c)2
-rw-r--r--engines/sword25/util/lua/ltable.cpp (renamed from engines/sword25/util/lua/ltable.c)17
-rw-r--r--engines/sword25/util/lua/ltable.h2
-rw-r--r--engines/sword25/util/lua/ltablib.cpp (renamed from engines/sword25/util/lua/ltablib.c)2
-rw-r--r--engines/sword25/util/lua/ltm.cpp (renamed from engines/sword25/util/lua/ltm.c)2
-rw-r--r--engines/sword25/util/lua/ltm.h2
-rw-r--r--engines/sword25/util/lua/lua.c392
-rw-r--r--engines/sword25/util/lua/lua.h2
-rw-r--r--engines/sword25/util/lua/luac.c200
-rw-r--r--engines/sword25/util/lua/luaconf.h4
-rw-r--r--engines/sword25/util/lua/lualib.h2
-rw-r--r--engines/sword25/util/lua/lundump.cpp (renamed from engines/sword25/util/lua/lundump.c)2
-rw-r--r--engines/sword25/util/lua/lundump.h2
-rw-r--r--engines/sword25/util/lua/lvm.cpp (renamed from engines/sword25/util/lua/lvm.c)2
-rw-r--r--engines/sword25/util/lua/lvm.h2
-rw-r--r--engines/sword25/util/lua/lzio.cpp (renamed from engines/sword25/util/lua/lzio.c)2
-rw-r--r--engines/sword25/util/lua/lzio.h2
-rw-r--r--engines/sword25/util/lua/print.cpp (renamed from engines/sword25/util/lua/print.c)2
-rw-r--r--engines/sword25/util/pluto/pdep.cpp (renamed from engines/sword25/util/pluto/pdep.c)0
-rw-r--r--engines/sword25/util/pluto/pdep/lauxlib.h2
-rw-r--r--engines/sword25/util/pluto/pdep/ldo.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lfunc.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lgc.h2
-rw-r--r--engines/sword25/util/pluto/pdep/llimits.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lobject.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lopcodes.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lstate.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lstring.h2
-rw-r--r--engines/sword25/util/pluto/pdep/ltm.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lua.h2
-rw-r--r--engines/sword25/util/pluto/pdep/lzio.h2
-rw-r--r--engines/sword25/util/pluto/pluto.cpp (renamed from engines/sword25/util/pluto/pluto.c)9
-rw-r--r--engines/sword25/util/pluto/plzio.cpp (renamed from engines/sword25/util/pluto/plzio.c)2
-rw-r--r--engines/sword25/util/pluto/pptest.cpp (renamed from engines/sword25/util/pluto/pptest.c)0
-rw-r--r--engines/sword25/util/pluto/puptest.cpp (renamed from engines/sword25/util/pluto/puptest.c)0
-rw-r--r--engines/teenagent/animation.cpp2
-rw-r--r--engines/teenagent/detection.cpp13
-rw-r--r--engines/teenagent/inventory.cpp2
-rw-r--r--engines/teenagent/objects.cpp2
-rw-r--r--engines/teenagent/pack.cpp2
-rw-r--r--engines/teenagent/teenagent.cpp8
-rw-r--r--engines/teenagent/teenagent.h2
-rw-r--r--engines/testbed/config-params.cpp2
-rw-r--r--engines/testbed/config.cpp8
-rw-r--r--engines/testbed/config.h2
-rw-r--r--engines/testbed/detection.cpp11
-rw-r--r--engines/testbed/graphics.cpp4
-rw-r--r--engines/testbed/midi.cpp3
-rw-r--r--engines/testbed/midi.h2
-rw-r--r--engines/testbed/misc.cpp2
-rw-r--r--engines/testbed/sound.cpp17
-rw-r--r--engines/testbed/testbed.cpp4
-rw-r--r--engines/testbed/testsuite.cpp2
-rw-r--r--engines/tinsel/actors.cpp11
-rw-r--r--engines/tinsel/background.cpp6
-rw-r--r--engines/tinsel/background.h2
-rw-r--r--engines/tinsel/bg.cpp1
-rw-r--r--engines/tinsel/bmv.cpp5
-rw-r--r--engines/tinsel/coroutine.cpp86
-rw-r--r--engines/tinsel/coroutine.h47
-rw-r--r--engines/tinsel/cursor.cpp2
-rw-r--r--engines/tinsel/debugger.cpp5
-rw-r--r--engines/tinsel/detection.cpp31
-rw-r--r--engines/tinsel/detection_tables.h112
-rw-r--r--engines/tinsel/dialogs.cpp58
-rw-r--r--engines/tinsel/drives.cpp74
-rw-r--r--engines/tinsel/drives.h21
-rw-r--r--engines/tinsel/events.cpp4
-rw-r--r--engines/tinsel/faders.cpp4
-rw-r--r--engines/tinsel/font.cpp6
-rw-r--r--engines/tinsel/graphics.cpp6
-rw-r--r--engines/tinsel/handle.cpp10
-rw-r--r--engines/tinsel/heapmem.cpp5
-rw-r--r--engines/tinsel/mareels.cpp2
-rw-r--r--engines/tinsel/module.mk1
-rw-r--r--engines/tinsel/move.cpp2
-rw-r--r--engines/tinsel/music.cpp14
-rw-r--r--engines/tinsel/object.cpp4
-rw-r--r--engines/tinsel/palette.cpp66
-rw-r--r--engines/tinsel/palette.h5
-rw-r--r--engines/tinsel/pcode.cpp2
-rw-r--r--engines/tinsel/pdisplay.cpp22
-rw-r--r--engines/tinsel/play.cpp4
-rw-r--r--engines/tinsel/polygons.cpp2
-rw-r--r--engines/tinsel/rince.cpp8
-rw-r--r--engines/tinsel/saveload.cpp20
-rw-r--r--engines/tinsel/savescn.cpp2
-rw-r--r--engines/tinsel/savescn.h12
-rw-r--r--engines/tinsel/scene.cpp8
-rw-r--r--engines/tinsel/scene.h6
-rw-r--r--engines/tinsel/sched.cpp17
-rw-r--r--engines/tinsel/scn.cpp9
-rw-r--r--engines/tinsel/scroll.cpp3
-rw-r--r--engines/tinsel/sound.cpp13
-rw-r--r--engines/tinsel/strres.cpp4
-rw-r--r--engines/tinsel/sysvar.cpp4
-rw-r--r--engines/tinsel/text.cpp2
-rw-r--r--engines/tinsel/text.h2
-rw-r--r--engines/tinsel/timers.cpp2
-rw-r--r--engines/tinsel/tinlib.cpp72
-rw-r--r--engines/tinsel/tinsel.cpp17
-rw-r--r--engines/tinsel/tinsel.h13
-rw-r--r--engines/tinsel/token.cpp2
-rw-r--r--engines/toon/anim.cpp55
-rw-r--r--engines/toon/anim.h5
-rw-r--r--engines/toon/audio.cpp218
-rw-r--r--engines/toon/audio.h43
-rw-r--r--engines/toon/character.cpp161
-rw-r--r--engines/toon/character.h8
-rw-r--r--engines/toon/console.cpp43
-rw-r--r--engines/toon/console.h50
-rw-r--r--engines/toon/detection.cpp23
-rw-r--r--engines/toon/drew.cpp8
-rw-r--r--engines/toon/drew.h3
-rw-r--r--engines/toon/flux.cpp2
-rw-r--r--engines/toon/flux.h2
-rw-r--r--engines/toon/font.cpp45
-rw-r--r--engines/toon/font.h3
-rw-r--r--engines/toon/hotspot.cpp10
-rw-r--r--engines/toon/hotspot.h2
-rw-r--r--engines/toon/module.mk1
-rw-r--r--engines/toon/movie.cpp13
-rw-r--r--engines/toon/movie.h6
-rw-r--r--engines/toon/path.cpp83
-rw-r--r--engines/toon/path.h14
-rw-r--r--engines/toon/picture.cpp6
-rw-r--r--engines/toon/picture.h1
-rw-r--r--engines/toon/resource.cpp47
-rw-r--r--engines/toon/resource.h8
-rw-r--r--engines/toon/script.cpp20
-rw-r--r--engines/toon/script.h9
-rw-r--r--engines/toon/script_func.cpp78
-rw-r--r--engines/toon/script_func.h5
-rw-r--r--engines/toon/text.cpp6
-rw-r--r--engines/toon/toon.cpp509
-rw-r--r--engines/toon/toon.h58
-rw-r--r--engines/touche/console.cpp43
-rw-r--r--engines/touche/console.h50
-rw-r--r--engines/touche/detection.cpp14
-rw-r--r--engines/touche/module.mk1
-rw-r--r--engines/touche/resource.cpp2
-rw-r--r--engines/touche/touche.cpp7
-rw-r--r--engines/touche/touche.h9
-rw-r--r--engines/tucker/console.cpp43
-rw-r--r--engines/tucker/console.h50
-rw-r--r--engines/tucker/detection.cpp13
-rw-r--r--engines/tucker/module.mk1
-rw-r--r--engines/tucker/resource.cpp2
-rw-r--r--engines/tucker/tucker.cpp8
-rw-r--r--engines/tucker/tucker.h7
1062 files changed, 104190 insertions, 22575 deletions
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 22212675c7..9be406d714 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -244,18 +244,21 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
if (cleanupPirated(matches))
return detectedGames;
- // Use fallback detector if there were no matches by other means
if (matches.empty()) {
+ // Use fallback detector if there were no matches by other means
const ADGameDescription *fallbackDesc = fallbackDetect(fslist);
if (fallbackDesc != 0) {
GameDescriptor desc(toGameDescriptor(*fallbackDesc, params.list));
updateGameDescriptor(desc, fallbackDesc, params);
detectedGames.push_back(desc);
}
- } else for (uint i = 0; i < matches.size(); i++) { // Otherwise use the found matches
- GameDescriptor desc(toGameDescriptor(*matches[i], params.list));
- updateGameDescriptor(desc, matches[i], params);
- detectedGames.push_back(desc);
+ } else {
+ // Otherwise use the found matches
+ for (uint i = 0; i < matches.size(); i++) {
+ GameDescriptor desc(toGameDescriptor(*matches[i], params.list));
+ updateGameDescriptor(desc, matches[i], params);
+ detectedGames.push_back(desc);
+ }
}
return detectedGames;
@@ -354,7 +357,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
struct SizeMD5 {
int size;
- char md5[32+1];
+ Common::String md5;
};
typedef Common::HashMap<Common::String, SizeMD5, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SizeMD5Map;
@@ -371,7 +374,7 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz
printf("of the game you tried to add and its version/language/etc.:\n");
for (SizeMD5Map::const_iterator file = filesSizeMD5.begin(); file != filesSizeMD5.end(); ++file)
- printf(" {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5, file->_value.size);
+ printf(" {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size);
printf("\n");
}
@@ -446,34 +449,37 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
Common::String fname = fileDesc->fileName;
SizeMD5 tmp;
+ if (filesSizeMD5.contains(fname))
+ continue;
+
+ // FIXME/TODO: We don't handle the case that a file is listed as a regular
+ // file and as one with resource fork.
+
if (g->flags & ADGF_MACRESFORK) {
Common::MacResManager *macResMan = new Common::MacResManager();
if (macResMan->open(parent, fname)) {
- if (!macResMan->getResForkMD5(tmp.md5, params.md5Bytes))
- tmp.md5[0] = 0;
+ tmp.md5 = macResMan->computeResForkMD5AsString(params.md5Bytes);
tmp.size = macResMan->getResForkSize();
- debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5);
+ debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str());
filesSizeMD5[fname] = tmp;
}
delete macResMan;
} else {
- if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) {
+ if (allFiles.contains(fname)) {
debug(3, "+ %s", fname.c_str());
Common::File testFile;
if (testFile.open(allFiles[fname])) {
tmp.size = (int32)testFile.size();
- if (!md5_file_string(testFile, tmp.md5, params.md5Bytes))
- tmp.md5[0] = 0;
+ tmp.md5 = Common::computeStreamMD5AsString(testFile, params.md5Bytes);
} else {
tmp.size = -1;
- tmp.md5[0] = 0;
}
- debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5);
+ debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str());
filesSizeMD5[fname] = tmp;
}
}
@@ -502,6 +508,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
continue;
bool allFilesPresent = true;
+ int curFilesMatched = 0;
// Try to match all files for this game
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
@@ -513,8 +520,8 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
break;
}
- if (fileDesc->md5 != NULL && 0 != strcmp(fileDesc->md5, filesSizeMD5[tstr].md5)) {
- debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5);
+ if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) {
+ debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5.c_str());
fileMissing = true;
break;
}
@@ -526,12 +533,13 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
}
debug(3, "Matched file: %s", tstr.c_str());
+ curFilesMatched++;
}
// We found at least one entry with all required files present.
// That means that we got new variant of the game.
//
- // Wihtout this check we would have errorneous checksum display
+ // Without this check we would have erroneous checksum display
// where only located files will be enlisted.
//
// Potentially this could rule out variants where some particular file
@@ -544,22 +552,11 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
- // Count the number of matching files. Then, only keep those
- // entries which match a maximal amount of files.
- int curFilesMatched = 0;
- for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++)
- curFilesMatched++;
-
if (curFilesMatched > maxFilesMatched) {
debug(2, " ... new best match, removing all previous candidates");
maxFilesMatched = curFilesMatched;
- for (uint j = 0; j < matched.size();) {
- if (matched[j]->flags & ADGF_KEEPMATCH)
- ++j;
- else
- matched.remove_at(j);
- }
+ matched.clear(); // Remove any prior, lower ranked matches.
matched.push_back(g);
} else if (curFilesMatched == maxFilesMatched) {
matched.push_back(g);
@@ -622,7 +619,7 @@ static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParam
matchedDesc = agdesc;
maxNumMatchedFiles = numMatchedFiles;
- debug(4, "and overriden");
+ debug(4, "and overridden");
}
}
}
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 0ebb264f74..515127b23d 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -49,7 +49,6 @@ enum ADGameFlags {
ADGF_ADDENGLISH = (1 << 24), // always add English as language option
ADGF_MACRESFORK = (1 << 25), // the md5 for this entry will be calculated from the resource fork
ADGF_USEEXTRAASTITLE = (1 << 26), // Extra field value will be used as main game title, not gameid
- ADGF_KEEPMATCH = (1 << 27), // this entry is kept even when there are matched entries with more files
ADGF_DROPLANGUAGE = (1 << 28), // don't add language to gameid
ADGF_CD = (1 << 29), // add "-cd" to gameid
ADGF_DEMO = (1 << 30) // add "-demo" to gameid
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index 0646f321d4..885704b9c6 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -27,6 +27,7 @@
#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "common/savefile.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
@@ -360,12 +361,12 @@ int AgiEngine::agiInit() {
switch (getVersion() >> 12) {
case 2:
- report("Emulating Sierra AGI v%x.%03x\n",
+ debug("Emulating Sierra AGI v%x.%03x\n",
(int)(getVersion() >> 12) & 0xF,
(int)(getVersion()) & 0xFFF);
break;
case 3:
- report("Emulating Sierra AGI v%x.002.%03x\n",
+ debug("Emulating Sierra AGI v%x.002.%03x\n",
(int)(getVersion() >> 12) & 0xF,
(int)(getVersion()) & 0xFFF);
break;
@@ -382,10 +383,10 @@ int AgiEngine::agiInit() {
_game.sbuf = _game.sbuf256c;
if (_game.gameFlags & ID_AMIGA)
- report("Amiga padded game detected.\n");
+ debug(1, "Amiga padded game detected.");
if (_game.gameFlags & ID_AGDS)
- report("AGDS mode enabled.\n");
+ debug(1, "AGDS mode enabled.");
ec = _loader->init(); // load vol files, etc
@@ -459,9 +460,8 @@ int AgiEngine::agiLoadResource(int r, int n) {
if (i == errOK && getGameID() == GID_GOLDRUSH && r == rPICTURE && n == 147 && _game.dirPic[n].len == 1982) {
uint8 *pic = _game.pictures[n].rdata;
Common::MemoryReadStream picStream(pic, _game.dirPic[n].len);
- char md5str[32+1];
- Common::md5_file_string(picStream, md5str, _game.dirPic[n].len);
- if (scumm_stricmp(md5str, "1c685eb048656cedcee4eb6eca2cecea") == 0) {
+ Common::String md5str = Common::computeStreamMD5AsString(picStream, _game.dirPic[n].len);
+ if (md5str == "1c685eb048656cedcee4eb6eca2cecea") {
pic[0x042] = 0x4B; // 0x49 -> 0x4B
pic[0x043] = 0x66; // 0x26 -> 0x66
pic[0x204] = 0x68; // 0x28 -> 0x68
@@ -655,7 +655,7 @@ void AgiEngine::initialize() {
_game.state = STATE_LOADED;
debugC(2, kDebugLevelMain, "game loaded");
} else {
- report("Could not open AGI game");
+ warning("Could not open AGI game");
}
debugC(2, kDebugLevelMain, "Init sound");
@@ -703,7 +703,6 @@ Common::Error AgiBase::init() {
Common::Error AgiEngine::go() {
CursorMan.showMouse(true);
- report(" \nAGI engine %s is ready.\n", gScummVMVersion);
if (_game.state < STATE_LOADED) {
do {
mainCycle();
@@ -716,6 +715,33 @@ Common::Error AgiEngine::go() {
}
void AgiEngine::parseFeatures() {
+
+ /* FIXME: Seems this method doesn't really do anything. It might
+ be a leftover that could be removed, except that some of its
+ intended purpose may still need to be reimplemented.
+
+ [0:29] <Fingolfin> can you tell me what the point behind AgiEngine::parseFeatures() is?
+ [0:30] <_sev> when games are created with WAGI studio
+ [0:31] <_sev> it creates .wag site with game-specific features such as full game title, whether to use AGIMOUSE etc
+ [0:32] <Fingolfin> ... and the "features" config key is created by our detector based on the wag file, I guess?
+ [0:33] <_sev> yes
+ [0:33] <Fingolfin> it's just that I cant seem to find a place we do that
+ [0:33] <_sev> it is used for fallback
+ [0:34] <_sev> ah, perhaps it was not updated
+ [0:34] <Fingolfin> I only see us check the value, but never set it
+ [0:34] <Fingolfin> maybe I am grepping wrong, who knows :)
+ [0:44] <Fingolfin> _sev: so, unless I miss something, it seem that function does nothing right now
+ [0:45] <_sev> Fingolfin: it could be unfinished. It was part of GSoC 3 years ago
+ [0:45] <Fingolfin> well
+ [0:45] <_sev> I just don't remember
+ [0:45] <Fingolfin> but don't we just re-parse the wag when the game is loaded anyway?
+ [0:45] <_sev> but it documents the format
+ [0:45] <Fingolfin> the advanced meta engine would re-run the detector, wouldn't it?
+ [0:45] <_sev> yep
+ [0:47] <Fingolfin> so... shouldn't we at least add a comment to the function explaining what it does and that it's unfinished etc.? maybe add a TODO to the wiki?
+ [0:47] <Fingolfin> otherwise it might stay as it is for another 3 years :)
+ */
+
if (!ConfMan.hasKey("features"))
return;
@@ -747,13 +773,15 @@ void AgiEngine::parseFeatures() {
for (int i = 0; i < numFeatures; i++) {
for (const Flags *flag = flags; flag->name; flag++) {
if (!scumm_stricmp(feature[i], flag->name)) {
- debug(0, "Added feature: %s", flag->name);
+ debug(2, "Added feature: %s", flag->name);
setFeature(flag->flag);
break;
}
}
}
+
+ free(features);
}
} // End of namespace Agi
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 4df8824b0e..8ff7f6c35e 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -52,8 +52,12 @@ namespace Common { class RandomSource; }
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Early Sierra adventure games
+ * - many fan made games
+ * - Mickey's Space Adventure (Pre-AGI)
+ * - Winnie the Pooh in the Hundred Acre Wood (Pre-AGI)
+ * - Troll's Tale (Pre-AGI)
*/
namespace Agi {
@@ -232,8 +236,6 @@ enum AgiMouseButton {
kAgiMouseButtonMiddle // Middle mouse button
};
-#define report printf
-
enum GameId {
GID_AGI = 1
};
@@ -556,8 +558,8 @@ struct AgiGame {
int lineUserInput; /**< line to put user input on */
int lineMinPrint; /**< num lines to print on */
int cursorPos; /**< column where the input cursor is */
- uint8 inputBuffer[40]; /**< buffer for user input */
- uint8 echoBuffer[40]; /**< buffer for echo.line */
+ byte inputBuffer[40]; /**< buffer for user input */
+ byte echoBuffer[40]; /**< buffer for echo.line */
int keypress;
InputMode inputMode; /**< keyboard input mode */
diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp
index e5942455e2..370f48e018 100644
--- a/engines/agi/console.cpp
+++ b/engines/agi/console.cpp
@@ -152,7 +152,7 @@ bool Console::Cmd_Flags(int argc, const char **argv) {
for (j = 0; j < 10; j++, i++) {
DebugPrintf("%c ", _vm->getflag(i) ? 'T' : 'F');
}
- report("\n");
+ DebugPrintf("\n");
}
return true;
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 57bcabb188..86c1b519a9 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -319,12 +319,12 @@ int AgiEngine::playGame() {
// We run AGIMOUSE always as a side effect
if (getFeatures() & GF_AGIMOUSE || true)
- report("Using AGI Mouse 1.0 protocol\n");
+ debug(1, "Using AGI Mouse 1.0 protocol");
if (getFeatures() & GF_AGIPAL)
debug(1, "Running AGIPAL game");
- report("Running AGI script.\n");
+ debug(0, "Running AGI script.\n");
setflag(fEnteredCli, false);
setflag(fSaidAcceptedInput, false);
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 14ef169c48..6e9a996756 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -30,6 +30,7 @@
#include "common/file.h"
#include "common/savefile.h"
#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
#include "agi/agi.h"
#include "agi/preagi.h"
diff --git a/engines/agi/id.cpp b/engines/agi/id.cpp
index 77c527444b..2e12e45458 100644
--- a/engines/agi/id.cpp
+++ b/engines/agi/id.cpp
@@ -47,7 +47,7 @@ int AgiEngine::setupV2Game(int ver) {
if (getFeatures() & GF_AGDS)
setVersion(ver = 0x2440); // ALL AGDS games built for 2.440
- report("Setting up for version 0x%04X\n", ver);
+ debug(0, "Setting up for version 0x%04X", ver);
// 'quit' takes 0 args for 2.089
if (ver == 0x2089)
@@ -70,7 +70,7 @@ int AgiEngine::setupV2Game(int ver) {
int AgiEngine::setupV3Game(int ver) {
int ec = errOK;
- report("Setting up for version 0x%04X\n", ver);
+ debug(0, "Setting up for version 0x%04X", ver);
// 'unknown176' takes 1 arg for 3.002.086, not 0 args.
// 'unknown173' also takes 1 arg for 3.002.068, not 0 args.
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
index 62bcd5d8d8..344654128d 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -121,7 +121,6 @@ int AgiEngine::handleController(int key) {
if (_game.controllers[i].keycode == key) {
debugC(3, kDebugLevelInput, "event %d: key press", _game.controllers[i].controller);
_game.controllerOccured[_game.controllers[i].controller] = true;
- report("event AC:%i occurred\n", _game.controllers[i].controller);
return true;
}
}
diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index de6f8d0653..8780e1dab8 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -43,7 +43,7 @@ int AgiLoader_v2::loadDir(AgiDir *agid, const char *fname) {
uint32 flen;
uint i;
- report("Loading directory: %s\n", fname);
+ debug(0, "Loading directory: %s", fname);
if (!fp.open(fname)) {
return errBadFileOpen;
@@ -157,8 +157,7 @@ uint8 *AgiLoader_v2::loadVolRes(struct AgiDir *agid) {
exit(1);
}
} else {
- report("Error: bad signature %04x\n", sig);
- // fprintf (stderr, "ACK! BAD RESOURCE!!!\n");
+ warning("AgiLoader_v2::loadVolRes: bad signature %04x", sig);
return 0;
}
fp.close();
diff --git a/engines/agi/loader_v3.cpp b/engines/agi/loader_v3.cpp
index f145140768..18ea4cae7d 100644
--- a/engines/agi/loader_v3.cpp
+++ b/engines/agi/loader_v3.cpp
@@ -121,7 +121,7 @@ int AgiLoader_v3::init() {
}
if (!fp.open(path)) {
- printf("Failed to open \"%s\"\n", path.c_str());
+ warning("Failed to open '%s'", path.c_str());
return errBadFileOpen;
}
// build offset table for v3 directory format
diff --git a/engines/agi/objects.cpp b/engines/agi/objects.cpp
index 5d1866bea4..d942a2b538 100644
--- a/engines/agi/objects.cpp
+++ b/engines/agi/objects.cpp
@@ -46,9 +46,9 @@ int AgiEngine::decodeObjects(uint8 *mem, uint32 flen) {
// if so, its encrypted, else it is not
if (READ_LE_UINT16(mem) > flen) {
- report("Decrypting objects... ");
+ debugN(0, "Decrypting objects... ");
decrypt(mem, flen);
- report("done.\n");
+ debug(0, "done.");
}
// alloc memory for object list
@@ -74,12 +74,11 @@ int AgiEngine::decodeObjects(uint8 *mem, uint32 flen) {
if ((uint) offset < flen) {
(_objects + i)->name = (char *)strdup((const char *)mem + offset);
} else {
- printf("ERROR: object %i name beyond object filesize! "
- "(%04x > %04x)\n", i, offset, flen);
+ warning("object %i name beyond object filesize (%04x > %04x)", i, offset, flen);
(_objects + i)->name = strdup("");
}
}
- report("Reading objects: %d objects read.\n", _game.numObjects);
+ debug(0, "Reading objects: %d objects read.", _game.numObjects);
return errOK;
}
@@ -92,8 +91,7 @@ int AgiEngine::loadObjects(const char *fname) {
_objects = NULL;
_game.numObjects = 0;
- debugC(5, kDebugLevelResources, "(fname = %s)", fname);
- report("Loading objects: %s\n", fname);
+ debugC(5, kDebugLevelResources, "(Loading objects '%s')", fname);
if (!fp.open(fname))
return errBadFileOpen;
@@ -130,7 +128,7 @@ void AgiEngine::unloadObjects() {
void AgiEngine::objectSetLocation(unsigned int n, int i) {
if (n >= _game.numObjects) {
- report("Error: Can't access object %d.\n", n);
+ warning("AgiEngine::objectSetLocation: Can't access object %d.\n", n);
return;
}
_objects[n].location = i;
@@ -138,7 +136,7 @@ void AgiEngine::objectSetLocation(unsigned int n, int i) {
int AgiEngine::objectGetLocation(unsigned int n) {
if (n >= _game.numObjects) {
- report("Error: Can't access object %d.\n", n);
+ warning("AgiEngine::objectGetLocation: Can't access object %d.\n", n);
return 0;
}
return _objects[n].location;
@@ -146,7 +144,7 @@ int AgiEngine::objectGetLocation(unsigned int n) {
const char *AgiEngine::objectName(unsigned int n) {
if (n >= _game.numObjects) {
- report("Error: Can't access object %d.\n", n);
+ warning("AgiEngine::objectName: Can't access object %d.\n", n);
return "";
}
return _objects[n].name;
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 1627f03863..2ea53e57ad 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -492,7 +492,7 @@ void AgiEngine::cmd_init_joy(uint8 *p) { // do nothing
}
void AgiEngine::cmd_script_size(uint8 *p) {
- report("script.size(%d)\n", p0);
+ debug(0, "script.size(%d)", p0);
}
void AgiEngine::cmd_cancel_line(uint8 *p) {
@@ -609,7 +609,7 @@ void AgiEngine::cmd_set_simple(uint8 *p) {
void AgiEngine::cmd_pop_script(uint8 *p) {
if (getVersion() >= 0x2915) {
- report("pop.script\n");
+ debug(0, "pop.script");
}
}
@@ -621,7 +621,7 @@ void AgiEngine::cmd_hold_key(uint8 *p) {
void AgiEngine::cmd_discard_sound(uint8 *p) {
if (getVersion() >= 0x2936) {
- report("discard.sound\n");
+ debug(0, "discard.sound");
}
}
@@ -1101,7 +1101,7 @@ void AgiEngine::cmd_set_game_id(uint8 *p) {
else
_game.id[0] = 0;
- report("Game ID: \"%s\"\n", _game.id);
+ debug(0, "Game ID: \"%s\"", _game.id);
}
void AgiEngine::cmd_pause(uint8 *p) {
@@ -1453,7 +1453,7 @@ void AgiEngine::cmd_clear_text_rect(uint8 *p) {
}
void AgiEngine::cmd_toggle_monitor(uint8 *p) {
- report("toggle.monitor\n");
+ debug(0, "toggle.monitor");
}
void AgiEngine::cmd_echo_line(uint8 *p) {
@@ -1510,7 +1510,7 @@ void AgiEngine::cmd_push_script(uint8 *p) {
_game.vars[29] = _mouse.y;
} else {
if (getVersion() >= 0x2915) {
- report("push.script\n");
+ debug(0, "push.script");
}
}
}
@@ -1518,7 +1518,7 @@ void AgiEngine::cmd_push_script(uint8 *p) {
void AgiEngine::cmd_set_pri_base(uint8 *p) {
int i, x, pri;
- report("Priority base set to %d\n", p0);
+ debug(0, "Priority base set to %d", p0);
// _game.alt_pri = true;
x = (_HEIGHT - p0) * _HEIGHT / 10;
diff --git a/engines/agi/op_dbg.cpp b/engines/agi/op_dbg.cpp
index be10f6ee75..f3b4815790 100644
--- a/engines/agi/op_dbg.cpp
+++ b/engines/agi/op_dbg.cpp
@@ -275,11 +275,11 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
uint8 a, c, z;
if (str) {
- report(" %s\n", str);
+ debug(0, " %s", str);
return;
}
- report("%03d:%04x ", lognum, ip);
+ debugN(0, "%03d:%04x ", lognum, ip);
switch (*(code + ip)) {
case 0xFC:
@@ -289,7 +289,7 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
x = logicNamesIf;
if (_debug.opcodes) {
- report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+ debugN(0, "%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
" ",
(uint8)*(code + (0 + ip)) & 0xFF,
(uint8)*(code + (1 + ip)) & 0xFF,
@@ -301,7 +301,7 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
(uint8)*(code + (7 + ip)) & 0xFF,
(uint8)*(code + (8 + ip)) & 0xFF);
}
- report("%s ", (x + *(code + ip) - 0xFC)->name);
+ debugN(0, "%s ", (x + *(code + ip) - 0xFC)->name);
break;
default:
x = mode == lCOMMAND_MODE ? logicNamesCmd : logicNamesTest;
@@ -309,7 +309,7 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
c = (unsigned char)(x + *(code + ip))->argMask;
if (_debug.opcodes) {
- report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+ debugN(0, "%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
" ",
(uint8)*(code + (0 + ip)) & 0xFF,
(uint8)*(code + (1 + ip)) & 0xFF,
@@ -321,23 +321,23 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
(uint8)*(code + (7 + ip)) & 0xFF,
(uint8)*(code + (8 + ip)) & 0xFF);
}
- report("%s ", (x + *(code + ip))->name);
+ debugN(0, "%s ", (x + *(code + ip))->name);
for (z = 1; a > 0;) {
if (~c & 0x80) {
- report("%d", *(code + (ip + z)));
+ debugN(0, "%d", *(code + (ip + z)));
} else {
- report("v%d[%d]", *(code + (ip + z)), getvar(*(code + (ip + z))));
+ debugN(0, "v%d[%d]", *(code + (ip + z)), getvar(*(code + (ip + z))));
}
c <<= 1;
z++;
if (--a > 0)
- report(",");
+ debugN(0, ",");
}
break;
}
- report("\n");
+ debugN(0, "\n");
}
} // End of namespace Agi
diff --git a/engines/agi/preagi_mickey.cpp b/engines/agi/preagi_mickey.cpp
index ec8a1f4878..ea041a93c7 100644
--- a/engines/agi/preagi_mickey.cpp
+++ b/engines/agi/preagi_mickey.cpp
@@ -302,6 +302,8 @@ void Mickey::getMouseMenuSelRow(MSA_MENU menu, int *sel0, int *sel1, int iRow, i
if (y != IDI_MSA_ROW_MENU_1) return;
sel = sel1;
break;
+ default:
+ return;
}
for (iWord = 0; iWord < menu.row[iRow].count; iWord++) {
@@ -1234,7 +1236,7 @@ int Mickey::getPlanet() {
if (!_game.nButtons)
return -1;
- for (int iPlanet = 0; iPlanet < IDI_MSA_MAX_DAT; iPlanet++) {
+ for (int iPlanet = 0; iPlanet < IDI_MSA_MAX_DAT - 1; iPlanet++) {
if (!strcmp(IDS_MSA_ADDR_PLANET[iPlanet], _game.szAddr)) {
return iPlanet;
}
@@ -1313,7 +1315,7 @@ void Mickey::flipSwitch() {
_game.iPlanetXtal[0] = IDI_MSA_PLANET_EARTH;
_game.iPlanetXtal[8] = IDI_MSA_PLANET_URANUS;
- for (int i = 1; i < 9; i++) {
+ for (int i = 1; i < IDI_MSA_MAX_PLANET; i++) {
if (i < 8) {
do {
// Earth (planet 0) and Uranus (planet 8) are excluded
diff --git a/engines/agi/preagi_winnie.cpp b/engines/agi/preagi_winnie.cpp
index 14d90f3211..643cbd86a9 100644
--- a/engines/agi/preagi_winnie.cpp
+++ b/engines/agi/preagi_winnie.cpp
@@ -30,6 +30,7 @@
#include "graphics/cursorman.h"
#include "common/events.h"
+#include "common/memstream.h"
#include "common/savefile.h"
namespace Agi {
diff --git a/engines/agi/predictive.cpp b/engines/agi/predictive.cpp
index 414477a381..250ac1df22 100644
--- a/engines/agi/predictive.cpp
+++ b/engines/agi/predictive.cpp
@@ -546,7 +546,7 @@ void AgiEngine::loadDict() {
#endif
uint32 time3 = _system->getMillis();
- printf("Time to parse pred.dic: %d, total: %d\n", time3-time2, time3-time1);
+ debug("Time to parse pred.dic: %d, total: %d", time3-time2, time3-time1);
}
bool AgiEngine::matchWord() {
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index 1a968816d4..94df063609 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -29,9 +29,10 @@
//
#include "common/file.h"
-#include "graphics/thumbnail.h"
#include "common/config-manager.h"
#include "common/savefile.h"
+#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
#include "agi/agi.h"
#include "agi/graphics.h"
diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp
index cc1cd0f6d5..4ed4fb7bab 100644
--- a/engines/agi/sound_2gs.cpp
+++ b/engines/agi/sound_2gs.cpp
@@ -26,6 +26,7 @@
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/md5.h"
+#include "common/memstream.h"
#include "common/str-array.h"
#include "agi/agi.h"
@@ -839,7 +840,7 @@ bool SoundGen2GS::loadInstrumentHeaders(const Common::FSNode &exePath, const IIg
}
// Read the whole executable file into memory
- Common::SharedPtr<Common::MemoryReadStream> data(file.readStream(file.size()));
+ Common::SharedPtr<Common::SeekableReadStream> data(file.readStream(file.size()));
file.close();
// Check that we got enough data to be able to parse the instruments
@@ -855,11 +856,10 @@ bool SoundGen2GS::loadInstrumentHeaders(const Common::FSNode &exePath, const IIg
// Check instrument set's md5sum
data->seek(exeInfo.instSetStart);
- char md5str[32+1];
- Common::md5_file_string(*data, md5str, exeInfo.instSet.byteCount);
- if (scumm_stricmp(md5str, exeInfo.instSet.md5)) {
+ Common::String md5str = Common::computeStreamMD5AsString(*data, exeInfo.instSet.byteCount);
+ if (md5str != exeInfo.instSet.md5) {
warning("Unknown Apple IIGS instrument set (md5: %s) in %s, trying to use it nonetheless",
- md5str, exePath.getPath().c_str());
+ md5str.c_str(), exePath.getPath().c_str());
}
// Read in the instrument set one instrument at a time
@@ -892,18 +892,17 @@ bool SoundGen2GS::loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo
// Open the wave file and read it into memory
file.open(wavePath);
- Common::SharedPtr<Common::MemoryReadStream> uint8Wave(file.readStream(file.size()));
+ Common::SharedPtr<Common::SeekableReadStream> uint8Wave(file.readStream(file.size()));
file.close();
// Check that we got the whole wave file
if (uint8Wave && uint8Wave->size() == SIERRASTANDARD_SIZE) {
// Check wave file's md5sum
- char md5str[32+1];
- Common::md5_file_string(*uint8Wave, md5str, SIERRASTANDARD_SIZE);
- if (scumm_stricmp(md5str, exeInfo.instSet.waveFileMd5)) {
+ Common::String md5str = Common::computeStreamMD5AsString(*uint8Wave, SIERRASTANDARD_SIZE);
+ if (md5str != exeInfo.instSet.waveFileMd5) {
warning("Unknown Apple IIGS wave file (md5: %s, game: %s).\n" \
"Please report the information on the previous line to the ScummVM team.\n" \
- "Using the wave file as it is - music may sound weird", md5str, exeInfo.exePrefix);
+ "Using the wave file as it is - music may sound weird", md5str.c_str(), exeInfo.exePrefix);
}
uint8Wave->seek(0); // Seek wave to its start
diff --git a/engines/agi/sound_coco3.cpp b/engines/agi/sound_coco3.cpp
index f054be0682..858c1c8515 100644
--- a/engines/agi/sound_coco3.cpp
+++ b/engines/agi/sound_coco3.cpp
@@ -61,9 +61,9 @@ void SoundGenCoCo3::play(int resnum) {
uint32 start_time = _vm->_system->getMillis();
while (_vm->_system->getMillis() < start_time + note.duration) {
- _vm->_system->updateScreen();
+ _vm->_system->updateScreen();
- _vm->_system->delayMillis(10);
+ _vm->_system->delayMillis(10);
}
}
} while (note.freq != 0xff);
diff --git a/engines/agi/sound_midi.cpp b/engines/agi/sound_midi.cpp
index 0c8b3fa36a..9011ecaa86 100644
--- a/engines/agi/sound_midi.cpp
+++ b/engines/agi/sound_midi.cpp
@@ -47,6 +47,7 @@
#include "sound/midiparser.h"
#include "common/config-manager.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "common/stream.h"
#include "agi/agi.h"
@@ -294,7 +295,7 @@ static uint32 convertSND2MIDI(byte *snddata, byte **data) {
for (n = 0; n < 3; n++) {
uint16 start, end, pos;
-
+
st.write("MTrk", 4);
lp = st.pos();
st.writeUint32BE(0); /* chunklength */
diff --git a/engines/agi/sound_sarien.cpp b/engines/agi/sound_sarien.cpp
index 08bdd47497..4ede50a749 100644
--- a/engines/agi/sound_sarien.cpp
+++ b/engines/agi/sound_sarien.cpp
@@ -95,13 +95,10 @@ SoundGenSarien::SoundGenSarien(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(v
break;
}
- report("Initializing sound:\n");
-
- report("sound: envelopes ");
if (_env) {
- report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
+ debug(0, "Initializing sound: envelopes enabled (decay=%d, sustain=%d)", ENV_DECAY, ENV_SUSTAIN);
} else {
- report("disabled\n");
+ debug(0, "Initializing sound: envelopes disabled");
}
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index 464c218ae8..c48ed90ad8 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -52,10 +52,10 @@ int AgiEngine::loadWords(const char *fname) {
words = NULL;
if (!fp.open(fname)) {
- report("Warning: can't open %s\n", fname);
+ warning("loadWords: can't open %s", fname);
return errOK; // err_BadFileOpen
}
- report("Loading dictionary: %s\n", fname);
+ debug(0, "Loading dictionary: %s", fname);
fp.seek(0, SEEK_END);
flen = fp.pos();
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 0c8e6dd63c..7250c7b957 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -37,13 +37,13 @@
#include "agos/agos.h"
#include "agos/vga.h"
+#include "backends/audiocd/audiocd.h"
+
#include "graphics/surface.h"
#include "sound/mididrv.h"
#include "sound/mods/protracker.h"
-using Common::File;
-
namespace AGOS {
static const GameSpecificSettings simon1_settings = {
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index b12bf09d62..4cdd56dc8d 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -47,8 +47,13 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Elvira: Mistress of the Dark
+ * - Elvira 2: The Jaws of Cerberus
+ * - The Feeble Files
+ * - Simon the Sorcerer
+ * - Simon the Sorcerer 2
+ * - Simon the Sorcerer Puzzle Pack
*/
namespace AGOS {
@@ -1922,7 +1927,7 @@ public:
void off_setPathValues();
void off_stopClock();
void off_restartClock();
- void off_setColour();
+ void off_setColor();
void off_b3Set();
void off_b3Clear();
void off_b3Zero();
@@ -2093,6 +2098,8 @@ protected:
void startOverlayAnims();
void startAnOverlayAnim();
+ void printInfoText(const char *itemText);
+
virtual char *genSaveName(int slot);
};
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp
index af85c50114..4fe8f8e6bc 100644
--- a/engines/agos/animation.cpp
+++ b/engines/agos/animation.cpp
@@ -250,7 +250,7 @@ bool MoviePlayerDXA::load() {
}
}
- Common::String videoName = Common::String::printf("%s.dxa", baseName);
+ Common::String videoName = Common::String::format("%s.dxa", baseName);
if (!loadFile(videoName))
error("Failed to load video file %s", videoName.c_str());
@@ -410,7 +410,7 @@ MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name)
}
bool MoviePlayerSMK::load() {
- Common::String videoName = Common::String::printf("%s.smk", baseName);
+ Common::String videoName = Common::String::format("%s.smk", baseName);
if (!loadFile(videoName))
error("Failed to load video file %s", videoName.c_str());
diff --git a/engines/agos/debug.cpp b/engines/agos/debug.cpp
index 519dfc344c..cb11d65218 100644
--- a/engines/agos/debug.cpp
+++ b/engines/agos/debug.cpp
@@ -76,30 +76,30 @@ const byte *AGOSEngine::dumpOpcode(const byte *p) {
while (*st != '|')
st++;
- printf("%s ", st + 1);
+ debugN("%s ", st + 1);
for (;;) {
switch (*s++) {
case 'x':
- printf("\n");
+ debugN("\n");
return NULL;
case '|':
- printf("\n");
+ debugN("\n");
return p;
case 'B':{
byte b = *p++;
if (b == 255)
- printf("[%d] ", *p++);
+ debugN("[%d] ", *p++);
else
- printf("%d ", b);
+ debugN("%d ", b);
break;
}
case 'V':{
byte b = *p++;
if (b == 255)
- printf("[[%d]] ", *p++);
+ debugN("[[%d]] ", *p++);
else
- printf("[%d] ", b);
+ debugN("[%d] ", b);
break;
}
@@ -108,15 +108,15 @@ const byte *AGOSEngine::dumpOpcode(const byte *p) {
p += 2;
if (getGameType() == GType_PP) {
if (n >= 60000 && n < 62048)
- printf("[%d] ", n - 60000);
+ debugN("[%d] ", n - 60000);
else
- printf("%d ", n);
+ debugN("%d ", n);
} else {
if (n >= 30000 && n < 30512)
- printf("[%d] ", n - 30000);
+ debugN("[%d] ", n - 30000);
else
- printf("%d ", n);
+ debugN("%d ", n);
}
break;
}
@@ -124,7 +124,7 @@ const byte *AGOSEngine::dumpOpcode(const byte *p) {
case 'w':{
int n = (int16)READ_BE_UINT16(p);
p += 2;
- printf("%d ", n);
+ debugN("%d ", n);
break;
}
@@ -132,22 +132,22 @@ const byte *AGOSEngine::dumpOpcode(const byte *p) {
int n = (int16)READ_BE_UINT16(p);
p += 2;
if (n == -1)
- printf("SUBJECT_ITEM ");
+ debugN("SUBJECT_ITEM ");
else if (n == -3)
- printf("OBJECT_ITEM ");
+ debugN("OBJECT_ITEM ");
else if (n == -5)
- printf("ME_ITEM ");
+ debugN("ME_ITEM ");
else if (n == -7)
- printf("ACTOR_ITEM ");
+ debugN("ACTOR_ITEM ");
else if (n == -9)
- printf("ITEM_A_PARENT ");
+ debugN("ITEM_A_PARENT ");
else
- printf("<%d> ", n);
+ debugN("<%d> ", n);
break;
}
case 'J':{
- printf("-> ");
+ debugN("-> ");
}
break;
@@ -155,9 +155,9 @@ const byte *AGOSEngine::dumpOpcode(const byte *p) {
uint n = READ_BE_UINT16(p);
p += 2;
if (n != 0xFFFF)
- printf("\"%s\"(%d) ", getStringPtrByID(n), n);
+ debugN("\"%s\"(%d) ", getStringPtrByID(n), n);
else
- printf("NULL_STRING ");
+ debugN("NULL_STRING ");
}
break;
}
@@ -167,11 +167,11 @@ const byte *AGOSEngine::dumpOpcode(const byte *p) {
void AGOSEngine::dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub) {
const byte *p;
- printf("; ****\n");
+ debugN("; ****\n");
p = (byte *)sl + SUBROUTINE_LINE_SMALL_SIZE;
if (sub->id == 0) {
- printf("; verb=%d, noun1=%d, noun2=%d\n", sl->verb, sl->noun1, sl->noun2);
+ debugN("; verb=%d, noun1=%d, noun2=%d\n", sl->verb, sl->noun1, sl->noun2);
p = (byte *)sl + SUBROUTINE_LINE_BIG_SIZE;
}
@@ -185,12 +185,12 @@ void AGOSEngine::dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub) {
void AGOSEngine::dumpSubroutine(Subroutine *sub) {
SubroutineLine *sl;
- printf("\n******************************************\n;Subroutine, ID=%d:\nSUB_%d:\n", sub->id, sub->id);
+ debugN("\n******************************************\n;Subroutine, ID=%d:\nSUB_%d:\n", sub->id, sub->id);
sl = (SubroutineLine *)((byte *)sub + sub->first);
for (; (byte *)sl != (byte *)sub; sl = (SubroutineLine *)((byte *)sub + sl->next)) {
dumpSubroutineLine(sl, sub);
}
- printf("\nEND ******************************************\n");
+ debugN("\nEND ******************************************\n");
}
void AGOSEngine::dumpSubroutines() {
@@ -245,35 +245,35 @@ void AGOSEngine::dumpVideoScript(const byte *src, bool singeOpcode) {
while (*strn != '|')
strn++;
- printf("%.2d: %s ", opcode, strn + 1);
+ debugN("%.2d: %s ", opcode, strn + 1);
int end = (getGameType() == GType_FF || getGameType() == GType_PP) ? 9999 : 999;
for (; *str != '|'; str++) {
switch (*str) {
case 'x':
- printf("\n");
+ debugN("\n");
return;
case 'b':
- printf("%d ", *src++);
+ debugN("%d ", *src++);
break;
case 'd':
- printf("%d ", (int16)readUint16Wrapper(src));
+ debugN("%d ", (int16)readUint16Wrapper(src));
src += 2;
break;
case 'v':
- printf("[%d] ", readUint16Wrapper(src));
+ debugN("[%d] ", readUint16Wrapper(src));
src += 2;
break;
case 'i':
- printf("%d ", (int16)readUint16Wrapper(src));
+ debugN("%d ", (int16)readUint16Wrapper(src));
src += 2;
break;
case 'j':
- printf("-> ");
+ debugN("-> ");
break;
case 'q':
while (readUint16Wrapper(src) != end) {
- printf("(%d,%d) ", readUint16Wrapper(src),
+ debugN("(%d,%d) ", readUint16Wrapper(src),
readUint16Wrapper(src + 2));
src += 4;
}
@@ -284,7 +284,7 @@ void AGOSEngine::dumpVideoScript(const byte *src, bool singeOpcode) {
}
}
- printf("\n");
+ debugN("\n");
} while (!singeOpcode);
}
@@ -293,10 +293,10 @@ void AGOSEngine::dumpVgaScript(const byte *ptr, uint16 res, uint16 id) {
}
void AGOSEngine::dumpVgaScriptAlways(const byte *ptr, uint16 res, uint16 id) {
- printf("; address=%x, vgafile=%d vgasprite=%d\n",
+ debugN("; address=%x, vgafile=%d vgasprite=%d\n",
(unsigned int)(ptr - _vgaBufferPointers[res].vgaFile1), res, id);
dumpVideoScript(ptr, false);
- printf("; end\n");
+ debugN("; end\n");
}
void AGOSEngine::dumpAllVgaImageFiles() {
diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp
index 32c26752a2..b937685b98 100644
--- a/engines/agos/event.cpp
+++ b/engines/agos/event.cpp
@@ -32,10 +32,13 @@
#include "common/events.h"
#include "common/system.h"
+#include "backends/audiocd/audiocd.h"
+
#include "gui/about.h"
#include "graphics/surface.h"
+
namespace AGOS {
void AGOSEngine::addTimeEvent(uint16 timeout, uint16 subroutine_id) {
diff --git a/engines/agos/menus.cpp b/engines/agos/menus.cpp
index df837a5e16..5629a1dca6 100644
--- a/engines/agos/menus.cpp
+++ b/engines/agos/menus.cpp
@@ -33,8 +33,6 @@
#include "agos/agos.h"
#include "agos/intern.h"
-using Common::File;
-
namespace AGOS {
void AGOSEngine::loadMenuFile() {
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index 858307685c..fe2d1cd25b 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -226,7 +226,7 @@ void MidiPlayer::startTrack(int track) {
parser->setMidiDriver(this);
parser->setTimerRate(_driver->getBaseTempo());
if (!parser->loadMusic(_music.songs[track], _music.song_sizes[track])) {
- printf ("Error reading track!\n");
+ warning("Error reading track %d", track);
delete parser;
parser = 0;
}
@@ -455,7 +455,7 @@ void MidiPlayer::loadSMF(Common::File *in, int song, bool sfx) {
parser->setMidiDriver(this);
parser->setTimerRate(timerRate);
if (!parser->loadMusic(p->data, size)) {
- printf("Error reading track!\n");
+ warning("Error reading track");
delete parser;
parser = 0;
}
@@ -484,7 +484,7 @@ void MidiPlayer::loadMultipleSMF(Common::File *in, bool sfx) {
p->num_songs = in->readByte();
if (p->num_songs > 16) {
- printf ("playMultipleSMF: %d is too many songs to keep track of!\n", (int)p->num_songs);
+ warning("playMultipleSMF: %d is too many songs to keep track of", (int)p->num_songs);
return;
}
@@ -496,7 +496,7 @@ void MidiPlayer::loadMultipleSMF(Common::File *in, bool sfx) {
// Make sure there's a MThd
in->read(buf, 4);
if (memcmp(buf, "MThd", 4)) {
- printf("Expected MThd but found '%c%c%c%c' instead!\n", buf[0], buf[1], buf[2], buf[3]);
+ warning("Expected MThd but found '%c%c%c%c' instead", buf[0], buf[1], buf[2], buf[3]);
return;
}
in->seek(in->readUint32BE(), SEEK_CUR);
diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp
index cb48e5e50b..2d99f7bcef 100644
--- a/engines/agos/res.cpp
+++ b/engines/agos/res.cpp
@@ -27,6 +27,7 @@
#include "common/file.h"
+#include "common/memstream.h"
#include "common/util.h"
#include "agos/agos.h"
@@ -35,8 +36,6 @@
#include "common/zlib.h"
-using Common::File;
-
namespace AGOS {
#ifdef ENABLE_AGOS2
@@ -67,7 +66,7 @@ uint32 AGOSEngine::readUint32Wrapper(const void *src) {
void AGOSEngine::decompressData(const char *srcName, byte *dst, uint32 offset, uint32 srcSize, uint32 dstSize) {
#ifdef USE_ZLIB
- File in;
+ Common::File in;
in.open(srcName);
if (in.isOpen() == false)
error("decompressData: Can't load %s", srcName);
@@ -548,7 +547,7 @@ uint fileReadItemID(Common::SeekableReadStream *in) {
}
void AGOSEngine::openGameFile() {
- _gameFile = new File();
+ _gameFile = new Common::File();
_gameFile->open(getFileName(GAME_GMEFILE));
if (!_gameFile->isOpen())
@@ -783,7 +782,7 @@ void AGOSEngine::loadVGABeardFile(uint16 id) {
uint32 offs, size;
if (getFeatures() & GF_OLD_BUNDLE) {
- File in;
+ Common::File in;
char filename[15];
if (id == 23)
id = 112;
@@ -824,7 +823,7 @@ void AGOSEngine::loadVGABeardFile(uint16 id) {
}
void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) {
- File in;
+ Common::File in;
char filename[15];
byte *dst;
uint32 file, offs, srcSize, dstSize;
diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp
index c6417962fb..7f11367d0f 100644
--- a/engines/agos/res_snd.cpp
+++ b/engines/agos/res_snd.cpp
@@ -25,17 +25,18 @@
#include "common/config-manager.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "agos/intern.h"
#include "agos/agos.h"
#include "agos/vga.h"
+#include "backends/audiocd/audiocd.h"
+
#include "sound/audiostream.h"
#include "sound/mididrv.h"
#include "sound/mods/protracker.h"
-using Common::File;
-
namespace AGOS {
void AGOSEngine_Simon1::playSpeech(uint16 speech_id, uint16 vgaSpriteId) {
@@ -174,7 +175,7 @@ static const ModuleOffs amigaWaxworksOffs[20] = {
void AGOSEngine::playModule(uint16 music) {
char filename[15];
- File f;
+ Common::File f;
uint32 offs = 0;
if (getPlatform() == Common::kPlatformAmiga && getGameType() == GType_WW) {
@@ -261,7 +262,7 @@ void AGOSEngine_Simon1::playMusic(uint16 music, uint16 track) {
// TODO: Add support for Desktop Tracker format in Acorn disk version
} else {
char filename[15];
- File f;
+ Common::File f;
sprintf(filename, "MOD%d.MUS", music);
f.open(filename);
if (f.isOpen() == false)
@@ -290,7 +291,7 @@ void AGOSEngine::playMusic(uint16 music, uint16 track) {
_midi.setLoop(true); // Must do this BEFORE loading music.
char filename[15];
- File f;
+ Common::File f;
sprintf(filename, "MOD%d.MUS", music);
f.open(filename);
if (f.isOpen() == false)
@@ -315,7 +316,7 @@ void AGOSEngine::playSting(uint16 soundId) {
char filename[15];
- File mus_file;
+ Common::File mus_file;
uint16 mus_offset;
sprintf(filename, "STINGS%i.MUS", _soundFileId);
@@ -346,7 +347,7 @@ static const byte elvira1_soundTable[100] = {
};
bool AGOSEngine::loadVGASoundFile(uint16 id, uint8 type) {
- File in;
+ Common::File in;
char filename[15];
byte *dst;
uint32 srcSize, dstSize;
@@ -451,7 +452,7 @@ static const char *dimpSoundList[32] = {
void AGOSEngine::loadSoundFile(const char* filename) {
- File in;
+ Common::File in;
in.open(filename);
if (in.isOpen() == false)
@@ -470,7 +471,7 @@ void AGOSEngine::loadSound(uint16 sound, int16 pan, int16 vol, uint16 type) {
byte *dst;
if (getGameId() == GID_DIMP) {
- File in;
+ Common::File in;
char filename[15];
assert(sound >= 1 && sound <= 32);
diff --git a/engines/agos/rooms.cpp b/engines/agos/rooms.cpp
index 91c8a53681..a2eff06fcc 100644
--- a/engines/agos/rooms.cpp
+++ b/engines/agos/rooms.cpp
@@ -30,24 +30,22 @@
#include "agos/agos.h"
#include "agos/intern.h"
-using Common::File;
-
namespace AGOS {
uint16 AGOSEngine::getBackExit(int n) {
switch (n) {
- case 0:
- return 2;
- case 1:
- return 3;
- case 2:
- return 0;
- case 3:
- return 1;
- case 4:
- return 5;
- case 5:
- return 4;
+ case 0:
+ return 2;
+ case 1:
+ return 3;
+ case 2:
+ return 0;
+ case 3:
+ return 1;
+ case 4:
+ return 5;
+ case 5:
+ return 4;
}
return 0;
@@ -205,13 +203,13 @@ void AGOSEngine_Elvira2::moveDirn(Item *i, uint x) {
if (n == 1) {
sr = (SubSuperRoom *)findChildOfType(p, kSuperRoomType);
switch (x) {
- case 0: a = -(sr->roomX); break;
- case 1: a = 1; break;
- case 2: a = sr->roomX; break;
- case 3: a = 0xFFFF; break;
- case 4: a = -(sr->roomX * sr->roomY); break;
- case 5: a = (sr->roomX * sr->roomY); break;
- default: return;
+ case 0: a = -(sr->roomX); break;
+ case 1: a = 1; break;
+ case 2: a = sr->roomX; break;
+ case 3: a = 0xFFFF; break;
+ case 4: a = -(sr->roomX * sr->roomY); break;
+ case 5: a = (sr->roomX * sr->roomY); break;
+ default: return;
}
_superRoomNumber += a;
}
@@ -366,7 +364,7 @@ bool AGOSEngine::loadRoomItems(uint16 room) {
byte *p;
uint i, minNum, maxNum;
char filename[30];
- File in;
+ Common::File in;
Item *item, *itemTmp;
if (_roomsList == NULL)
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index ffa95506c5..eefa2460ec 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -154,7 +154,7 @@ void AGOSEngine::quickLoadOrSave() {
Subroutine *sub;
success = loadGame(genSaveName(_saveLoadSlot));
if (!success) {
- buf = Common::String::printf(_("Failed to load game state from file:\n\n%s"), filename);
+ buf = Common::String::format(_("Failed to load game state from file:\n\n%s"), filename);
} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
drawIconArray(2, me(), 0, 0);
setBitFlag(97, true);
@@ -189,7 +189,7 @@ void AGOSEngine::quickLoadOrSave() {
} else {
success = saveGame(_saveLoadSlot, _saveLoadName);
if (!success)
- buf = Common::String::printf(_("Failed to save game state to file:\n\n%s"), filename);
+ buf = Common::String::format(_("Failed to save game state to file:\n\n%s"), filename);
}
if (!success) {
@@ -197,7 +197,7 @@ void AGOSEngine::quickLoadOrSave() {
dialog.runModal();
} else if (_saveLoadType == 1) {
- buf = Common::String::printf(_("Successfully saved game state in file:\n\n%s"), filename);
+ buf = Common::String::format(_("Successfully saved game state in file:\n\n%s"), filename);
GUI::TimedMessageDialog dialog(buf, 1500);
dialog.runModal();
diff --git a/engines/agos/script_dp.cpp b/engines/agos/script_dp.cpp
index 96e4beb880..57610017a1 100644
--- a/engines/agos/script_dp.cpp
+++ b/engines/agos/script_dp.cpp
@@ -281,7 +281,7 @@ void AGOSEngine_DIMP::setupOpcodes() {
OPCODE(opp_setPathValues),
OPCODE(off_stopClock),
OPCODE(off_restartClock),
- OPCODE(off_setColour),
+ OPCODE(off_setColor),
};
_opcodesDIMP = opcodes;
diff --git a/engines/agos/script_ff.cpp b/engines/agos/script_ff.cpp
index c1cc8ca9a0..5d640af6ad 100644
--- a/engines/agos/script_ff.cpp
+++ b/engines/agos/script_ff.cpp
@@ -282,7 +282,7 @@ void AGOSEngine_Feeble::setupOpcodes() {
OPCODE(off_setPathValues),
OPCODE(off_stopClock),
OPCODE(off_restartClock),
- OPCODE(off_setColour),
+ OPCODE(off_setColor),
/* 196 */
OPCODE(off_b3Set),
OPCODE(off_b3Clear),
@@ -638,8 +638,8 @@ void AGOSEngine_Feeble::off_restartClock() {
_clockStopped = 0;
}
-void AGOSEngine_Feeble::off_setColour() {
- // 195: set palette colour
+void AGOSEngine_Feeble::off_setColor() {
+ // 195: set palette color
uint16 c = getVarOrByte() * 4;
uint8 r = getVarOrByte();
uint8 g = getVarOrByte();
diff --git a/engines/agos/script_pn.cpp b/engines/agos/script_pn.cpp
index 0391d67b31..909c051362 100644
--- a/engines/agos/script_pn.cpp
+++ b/engines/agos/script_pn.cpp
@@ -890,7 +890,7 @@ int AGOSEngine_PN::doline(int needsave) {
int myTag = ++_tagOfActiveDoline; // Obtain a unique tag for this doline invocation
_dolineReturnVal = 0;
- if (needsave)
+ if (_stackbase && needsave)
_stackbase->tagOfParentDoline = myTag;
do {
diff --git a/engines/agos/script_pp.cpp b/engines/agos/script_pp.cpp
index 01c467a07e..624a1bbeba 100644
--- a/engines/agos/script_pp.cpp
+++ b/engines/agos/script_pp.cpp
@@ -281,7 +281,7 @@ void AGOSEngine_PuzzlePack::setupOpcodes() {
OPCODE(opp_setPathValues),
OPCODE(off_restartClock),
OPCODE(opp_pauseClock),
- OPCODE(off_setColour),
+ OPCODE(off_setColor),
};
_opcodesPuzzlePack = opcodes;
@@ -336,13 +336,12 @@ void AGOSEngine_PuzzlePack::opp_loadMouseImage() {
void AGOSEngine_PuzzlePack::opp_message() {
// 63: show string nl
-
+ const byte *stringPtr = getStringPtrByID(getNextStringID());
if (getBitFlag(105)) {
// Swampy adventures
- getStringPtrByID(getNextStringID());
-// printInfoText(getStringPtrByID(getNextStringID()));
+ printInfoText((const char *)stringPtr);
} else {
- showMessageFormat("%s\n", getStringPtrByID(getNextStringID()));
+ showMessageFormat("%s\n", stringPtr);
}
}
diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp
index 6ec72814fa..20deb47be8 100644
--- a/engines/agos/sound.cpp
+++ b/engines/agos/sound.cpp
@@ -24,6 +24,7 @@
*/
#include "common/file.h"
+#include "common/memstream.h"
#include "common/util.h"
#include "agos/agos.h"
@@ -38,23 +39,21 @@
#include "sound/decoders/vorbis.h"
#include "sound/decoders/wave.h"
-using Common::File;
-
namespace AGOS {
#define SOUND_BIG_ENDIAN true
class BaseSound : Common::NonCopyable {
protected:
- File *_file;
+ Common::File *_file;
uint32 *_offsets;
Audio::Mixer *_mixer;
bool _freeOffsets;
DisposeAfterUse::Flag _disposeFile;
public:
- BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigEndian, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES);
- BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES);
+ BaseSound(Audio::Mixer *mixer, Common::File *file, uint32 base, bool bigEndian, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES);
+ BaseSound(Audio::Mixer *mixer, Common::File *file, uint32 *offsets, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES);
virtual ~BaseSound();
void playSound(uint sound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, bool loop, int vol = 0) {
@@ -64,7 +63,7 @@ public:
virtual Audio::AudioStream *makeAudioStream(uint sound) = 0;
};
-BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigEndian, DisposeAfterUse::Flag disposeFileAfterUse)
+BaseSound::BaseSound(Audio::Mixer *mixer, Common::File *file, uint32 base, bool bigEndian, DisposeAfterUse::Flag disposeFileAfterUse)
: _mixer(mixer), _file(file), _disposeFile(disposeFileAfterUse) {
uint res = 0;
@@ -98,7 +97,7 @@ BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigEndia
_offsets[res] = _file->size();
}
-BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, DisposeAfterUse::Flag disposeFileAfterUse)
+BaseSound::BaseSound(Audio::Mixer *mixer, Common::File *file, uint32 *offsets, DisposeAfterUse::Flag disposeFileAfterUse)
: _mixer(mixer), _file(file), _disposeFile(disposeFileAfterUse) {
_offsets = offsets;
@@ -225,9 +224,9 @@ static void convertPan(int &pan) {
class WavSound : public BaseSound {
public:
- WavSound(Audio::Mixer *mixer, File *file, uint32 base = 0, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES)
+ WavSound(Audio::Mixer *mixer, Common::File *file, uint32 base = 0, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES)
: BaseSound(mixer, file, base, false, disposeFileAfterUse) {}
- WavSound(Audio::Mixer *mixer, File *file, uint32 *offsets) : BaseSound(mixer, file, offsets) {}
+ WavSound(Audio::Mixer *mixer, Common::File *file, uint32 *offsets) : BaseSound(mixer, file, offsets) {}
Audio::AudioStream *makeAudioStream(uint sound);
void playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, bool loop, int vol = 0);
};
@@ -251,7 +250,7 @@ void WavSound::playSound(uint sound, uint loopSound, Audio::Mixer::SoundType typ
class VocSound : public BaseSound {
const byte _flags;
public:
- VocSound(Audio::Mixer *mixer, File *file, bool isUnsigned, uint32 base = 0, bool bigEndian = false, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES)
+ VocSound(Audio::Mixer *mixer, Common::File *file, bool isUnsigned, uint32 base = 0, bool bigEndian = false, DisposeAfterUse::Flag disposeFileAfterUse = DisposeAfterUse::YES)
: BaseSound(mixer, file, base, bigEndian, disposeFileAfterUse), _flags(isUnsigned ? Audio::FLAG_UNSIGNED : 0) {}
Audio::AudioStream *makeAudioStream(uint sound);
void playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, bool loop, int vol = 0);
@@ -275,7 +274,7 @@ void VocSound::playSound(uint sound, uint loopSound, Audio::Mixer::SoundType typ
class RawSound : public BaseSound {
const byte _flags;
public:
- RawSound(Audio::Mixer *mixer, File *file, bool isUnsigned)
+ RawSound(Audio::Mixer *mixer, Common::File *file, bool isUnsigned)
: BaseSound(mixer, file, 0, SOUND_BIG_ENDIAN), _flags(isUnsigned ? Audio::FLAG_UNSIGNED : 0) {}
Audio::AudioStream *makeAudioStream(uint sound);
void playSound(uint sound, uint loopSound, Audio::Mixer::SoundType type, Audio::SoundHandle *handle, bool loop, int vol = 0);
@@ -305,9 +304,9 @@ void RawSound::playSound(uint sound, uint loopSound, Audio::Mixer::SoundType typ
class CompressedSound : public BaseSound {
public:
- CompressedSound(Audio::Mixer *mixer, File *file, uint32 base) : BaseSound(mixer, file, base, false) {}
+ CompressedSound(Audio::Mixer *mixer, Common::File *file, uint32 base) : BaseSound(mixer, file, base, false) {}
- Common::MemoryReadStream *loadStream(uint sound) const {
+ Common::SeekableReadStream *loadStream(uint sound) const {
if (_offsets == NULL)
return NULL;
@@ -334,9 +333,9 @@ public:
#ifdef USE_MAD
class MP3Sound : public CompressedSound {
public:
- MP3Sound(Audio::Mixer *mixer, File *file, uint32 base = 0) : CompressedSound(mixer, file, base) {}
+ MP3Sound(Audio::Mixer *mixer, Common::File *file, uint32 base = 0) : CompressedSound(mixer, file, base) {}
Audio::AudioStream *makeAudioStream(uint sound) {
- Common::MemoryReadStream *tmp = loadStream(sound);
+ Common::SeekableReadStream *tmp = loadStream(sound);
if (!tmp)
return NULL;
return Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
@@ -350,9 +349,9 @@ public:
#ifdef USE_VORBIS
class VorbisSound : public CompressedSound {
public:
- VorbisSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : CompressedSound(mixer, file, base) {}
+ VorbisSound(Audio::Mixer *mixer, Common::File *file, uint32 base = 0) : CompressedSound(mixer, file, base) {}
Audio::AudioStream *makeAudioStream(uint sound) {
- Common::MemoryReadStream *tmp = loadStream(sound);
+ Common::SeekableReadStream *tmp = loadStream(sound);
if (!tmp)
return NULL;
return Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
@@ -366,9 +365,9 @@ public:
#ifdef USE_FLAC
class FLACSound : public CompressedSound {
public:
- FLACSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : CompressedSound(mixer, file, base) {}
+ FLACSound(Audio::Mixer *mixer, Common::File *file, uint32 base = 0) : CompressedSound(mixer, file, base) {}
Audio::AudioStream *makeAudioStream(uint sound) {
- Common::MemoryReadStream *tmp = loadStream(sound);
+ Common::SeekableReadStream *tmp = loadStream(sound);
if (!tmp)
return NULL;
return Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
@@ -379,7 +378,7 @@ public:
///////////////////////////////////////////////////////////////////////////////
#pragma mark -
-static CompressedSound *makeCompressedSound(Audio::Mixer *mixer, File *file, const Common::String &basename) {
+static CompressedSound *makeCompressedSound(Audio::Mixer *mixer, Common::File *file, const Common::String &basename) {
#ifdef USE_FLAC
file->open(basename + ".fla");
if (file->isOpen()) {
@@ -451,7 +450,7 @@ void Sound::loadVoiceFile(const GameSpecificSettings *gss) {
char filename[16];
- File *file = new File();
+ Common::File *file = new Common::File();
if (!_hasVoiceFile) {
_voice = makeCompressedSound(_mixer, file, gss->speech_filename);
@@ -506,7 +505,7 @@ void Sound::loadVoiceFile(const GameSpecificSettings *gss) {
void Sound::loadSfxFile(const GameSpecificSettings *gss) {
char filename[16];
- File *file = new File();
+ Common::File *file = new Common::File();
if (!_hasEffectsFile) {
_effects = makeCompressedSound(_mixer, file, gss->effects_filename);
@@ -540,7 +539,7 @@ void Sound::readSfxFile(const Common::String &filename) {
_mixer->stopHandle(_effectsHandle);
- File *file = new File();
+ Common::File *file = new Common::File();
file->open(filename);
if (file->isOpen() == false) {
@@ -557,7 +556,7 @@ void Sound::readSfxFile(const Common::String &filename) {
}
// This method is only used by Simon2
-void Sound::loadSfxTable(File *gameFile, uint32 base) {
+void Sound::loadSfxTable(Common::File *gameFile, uint32 base) {
stopAll();
delete _effects;
@@ -572,7 +571,7 @@ void Sound::loadSfxTable(File *gameFile, uint32 base) {
void Sound::readVoiceFile(const Common::String &filename) {
_mixer->stopHandle(_voiceHandle);
- File *file = new File();
+ Common::File *file = new Common::File();
file->open(filename);
if (file->isOpen() == false)
@@ -592,7 +591,7 @@ void Sound::playVoice(uint sound) {
char filename[16];
_lastVoiceFile = _filenums[sound];
sprintf(filename, "voices%d.dat", _filenums[sound]);
- File *file = new File();
+ Common::File *file = new Common::File();
file->open(filename);
if (file->isOpen() == false)
error("playVoice: Can't load voice file %s", filename);
@@ -777,7 +776,7 @@ void Sound::playVoiceData(byte *soundData, uint sound) {
void Sound::playSoundData(Audio::SoundHandle *handle, byte *soundData, uint sound, int pan, int vol, bool loop) {
int size = READ_LE_UINT32(soundData + 4) + 8;
- Common::MemoryReadStream *stream = new Common::MemoryReadStream(soundData, size);
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(soundData, size);
Audio::RewindableAudioStream *sndStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
convertVolume(vol);
@@ -801,7 +800,7 @@ void Sound::switchVoiceFile(const GameSpecificSettings *gss, uint disc) {
_lastVoiceFile = disc;
char filename[16];
- File *file = new File();
+ Common::File *file = new Common::File();
if (!_hasVoiceFile) {
sprintf(filename, "%s%d", gss->speech_filename, disc);
diff --git a/engines/agos/string.cpp b/engines/agos/string.cpp
index 40506e4874..ed56cbd266 100644
--- a/engines/agos/string.cpp
+++ b/engines/agos/string.cpp
@@ -27,11 +27,12 @@
#include "common/file.h"
+#include "gui/about.h"
+#include "gui/message.h"
+
#include "agos/agos.h"
#include "agos/intern.h"
-using Common::File;
-
namespace AGOS {
void AGOSEngine::uncompressText(byte *ptr) {
@@ -265,7 +266,7 @@ uint AGOSEngine::loadTextFile(const char *filename, byte *dst) {
}
uint AGOSEngine::loadTextFile_simon1(const char *filename, byte *dst) {
- File fo;
+ Common::File fo;
fo.open(filename);
uint32 size;
@@ -561,6 +562,162 @@ void AGOSEngine::printScreenText(uint vgaSpriteId, uint color, const char *strin
}
#ifdef ENABLE_AGOS2
+// Swampy Adventures specific
+void AGOSEngine_PuzzlePack::printInfoText(const char *itemText) {
+ const char *itemName = NULL;
+ int flag = (_mouse.y / 32) * 20 + (_mouse.x / 32) + 1300;
+
+ switch (_variableArray[999]) {
+ case 80:
+ if (_variableArray[flag]) {
+ if (_variableArray[flag] == 201)
+ itemName = " Bridge: ";
+ if (_variableArray[flag] == 231 || _variableArray[flag] == 241)
+ itemName = " Log: ";
+ if (_variableArray[flag] == 281)
+ itemName = " Rubble: ";
+ if (_variableArray[flag] == 291)
+ itemName = " Boulder: ";
+ if (_variableArray[flag] == 311)
+ itemName = " Key: ";
+ if (_variableArray[flag] == 312)
+ itemName = " Spanner: ";
+ if (_variableArray[flag] == 321)
+ itemName = " Gate: ";
+ if (_variableArray[flag] == 331)
+ itemName = " Crate: ";
+ } else {
+ flag -= 300;
+ if (_variableArray[flag] == 2)
+ itemName = " Water: ";
+ if (_variableArray[flag] == 5)
+ itemName = " Exit: ";
+ if ((_variableArray[flag] == 5) && (_variableArray[81] == 10))
+ itemName = " Gem: ";
+ if (_variableArray[flag] == 236 || _variableArray[flag] == 246)
+ itemName = " Floating Log: ";
+ if (_variableArray[flag] == 400)
+ itemName = " Valve: ";
+ }
+ break;
+
+ case 81:
+ if (_variableArray[flag]) {
+ if (_variableArray[flag] == 281)
+ itemName = " Cracked Block: ";
+ if (_variableArray[flag] == 291)
+ itemName = " Boulder: ";
+ if (_variableArray[flag] == 331)
+ itemName = " Block: ";
+ if (_variableArray[flag] == 341)
+ itemName = " Switch: ";
+ if (_variableArray[flag] == 343)
+ itemName = " Button: ";
+ if ((_variableArray[flag] > 430) && (_variableArray[flag] < 480))
+ itemName = " Mosaic Block: ";
+ } else {
+ flag -= 300;
+ if (_variableArray[flag] == 5)
+ itemName = " Exit: ";
+ if ((_variableArray[flag] == 5) && (_variableArray[82] == 10))
+ itemName = " Gem: ";
+ }
+ break;
+
+ case 82:
+ if (_variableArray[flag]) {
+ if (_variableArray[flag] == 201 || _variableArray[flag] == 211)
+ itemName = " Unstable Track: ";
+ if (_variableArray[flag] == 281)
+ itemName = " Rubble Pile: ";
+ if (_variableArray[flag] == 291)
+ itemName = " Boulder: ";
+ if (_variableArray[flag] == 331)
+ itemName = " Crate: ";
+ if (_variableArray[flag] == 401 || _variableArray[flag] == 405)
+ itemName = " Cart: ";
+ } else {
+ flag -= 300;
+ if (_variableArray[flag] == 4)
+ itemName = " Hole: ";
+ if (_variableArray[flag] == 5)
+ itemName = " Exit: ";
+ if ((_variableArray[flag] == 5) && (_variableArray[83] == 10))
+ itemName = " Gem: ";
+ if ((_variableArray[flag] > 5) && (_variableArray[flag] < 10))
+ itemName = " Buffer Track: ";
+ if ((_variableArray[flag] > 9) && (_variableArray[flag] < 40))
+ itemName = " Track: ";
+ if (_variableArray[flag] == 300)
+ itemName = " Boulder: ";
+ }
+ break;
+
+ case 83:
+ if (_variableArray[flag]) {
+ if (_variableArray[flag] == 201)
+ itemName = " Broken Floor: ";
+ if (_variableArray[flag] == 231 || _variableArray[flag] == 241)
+ itemName = " Barrel: ";
+ if (_variableArray[flag] == 281)
+ itemName = " Cracked Rock: ";
+ if (_variableArray[flag] == 291)
+ itemName = " Spacehopper: ";
+ if (_variableArray[flag] == 311)
+ itemName = " Key: ";
+ if (_variableArray[flag] == 321)
+ itemName = " Trapdoor: ";
+ if (_variableArray[flag] == 324)
+ itemName = " Trapdoor: ";
+ if (_variableArray[flag] == 331)
+ itemName = " Crate: ";
+ } else {
+ flag -= 300;
+ if (_variableArray[flag] == 4)
+ itemName = " Hole: ";
+ if (_variableArray[flag] == 239 || _variableArray[flag] == 249)
+ itemName = " Barrel: ";
+ }
+ break;
+
+ case 84:
+ if (_variableArray[flag]) {
+ if (_variableArray[flag] == 201)
+ itemName = " Floating Platform: ";
+ if (_variableArray[flag] == 231)
+ itemName = " Cauldron: ";
+ if (_variableArray[flag] == 281)
+ itemName = " Cracked Block: ";
+ if (_variableArray[flag] == 311 || _variableArray[flag] == 312)
+ itemName = " Key: ";
+ if (_variableArray[flag] == 321 || _variableArray[flag] == 361 || _variableArray[flag] == 371)
+ itemName = " Gate: ";
+ if (_variableArray[flag] == 331)
+ itemName = " Chest: ";
+ if (_variableArray[flag] == 332)
+ itemName = " Jewel: ";
+ if (_variableArray[flag] == 351 || _variableArray[flag] == 352)
+ itemName = " Babies: ";
+ } else {
+ flag -= 300;
+ if (_variableArray[flag] == 6)
+ itemName = " Slime: ";
+ if (_variableArray[flag] == 334)
+ itemName = " Chest: ";
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (itemName != NULL) {
+ Common::String buf = Common::String::format("%s\n%s", itemName, itemText);
+ GUI::TimedMessageDialog dialog(buf, 1500);
+ dialog.runModal();
+ }
+}
+
// The Feeble Files specific
void AGOSEngine_Feeble::printScreenText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
char convertedString[320];
diff --git a/engines/agos/subroutine.cpp b/engines/agos/subroutine.cpp
index 8dccded958..733d40e52d 100644
--- a/engines/agos/subroutine.cpp
+++ b/engines/agos/subroutine.cpp
@@ -30,8 +30,6 @@
#include "agos/agos.h"
#include "agos/intern.h"
-using Common::File;
-
namespace AGOS {
// Script opcodes to load into memory
@@ -262,22 +260,22 @@ void AGOSEngine::endCutscene() {
_runScriptReturn1 = true;
}
-File *AGOSEngine::openTablesFile(const char *filename) {
+Common::File *AGOSEngine::openTablesFile(const char *filename) {
if (getFeatures() & GF_OLD_BUNDLE)
return openTablesFile_simon1(filename);
else
return openTablesFile_gme(filename);
}
-File *AGOSEngine::openTablesFile_simon1(const char *filename) {
- File *fo = new File();
+Common::File *AGOSEngine::openTablesFile_simon1(const char *filename) {
+ Common::File *fo = new Common::File();
fo->open(filename);
if (fo->isOpen() == false)
error("openTablesFile: Can't open '%s'", filename);
return fo;
}
-File *AGOSEngine::openTablesFile_gme(const char *filename) {
+Common::File *AGOSEngine::openTablesFile_gme(const char *filename) {
uint res;
uint32 offs;
@@ -291,7 +289,7 @@ File *AGOSEngine::openTablesFile_gme(const char *filename) {
bool AGOSEngine::loadTablesIntoMem(uint16 subrId) {
byte *p;
uint16 min_num, max_num, file_num;
- File *in;
+ Common::File *in;
char filename[30];
if (_tblList == NULL)
@@ -340,7 +338,7 @@ bool AGOSEngine::loadTablesIntoMem(uint16 subrId) {
bool AGOSEngine_Waxworks::loadTablesIntoMem(uint16 subrId) {
byte *p;
uint min_num, max_num;
- File *in;
+ Common::File *in;
p = _tblList;
if (p == NULL)
@@ -407,7 +405,7 @@ bool AGOSEngine::loadXTablesIntoMem(uint16 subrId) {
int i;
uint min_num, max_num;
char filename[30];
- File *in;
+ Common::File *in;
p = _xtblList;
if (p == NULL)
@@ -457,7 +455,7 @@ bool AGOSEngine::loadXTablesIntoMem(uint16 subrId) {
return 0;
}
-void AGOSEngine::closeTablesFile(File *in) {
+void AGOSEngine::closeTablesFile(Common::File *in) {
if (getFeatures() & GF_OLD_BUNDLE) {
in->close();
delete in;
@@ -572,7 +570,7 @@ restart:
_codePtr += 8;
if (_dumpOpcodes)
- printf("; %d\n", sub->id);
+ debug("; %d", sub->id);
result = runScript();
if (result != 0) {
break;
diff --git a/engines/agos/verb_pn.cpp b/engines/agos/verb_pn.cpp
index 129e1dec0e..b36f634ec0 100644
--- a/engines/agos/verb_pn.cpp
+++ b/engines/agos/verb_pn.cpp
@@ -185,7 +185,7 @@ void AGOSEngine_PN::hitBox5(HitArea *ha) {
_mousePrintFG++;
_mouseString = (const char *)"take \0";
- _mouseString1 = getMessage(_objectName1, _dragStore->msg1);
+ _mouseString1 = _dragStore ? getMessage(_objectName1, _dragStore->msg1) : "";
if (_dragStore->flags & kOBFRoomBox)
_mouseString1 = (const char *)"all\r";
diff --git a/engines/agos/vga.cpp b/engines/agos/vga.cpp
index dd2df79c07..8b8b16c9bb 100644
--- a/engines/agos/vga.cpp
+++ b/engines/agos/vga.cpp
@@ -155,7 +155,7 @@ void AGOSEngine::runVgaScript() {
if (_dumpVgaOpcodes) {
if (_vcPtr != (const byte *)&_vcGetOutOfCode) {
- printf("%.5d %.5X: %5d %4d ", _vgaTickCounter, (unsigned int)(_vcPtr - _curVgaFile1), _vgaCurSpriteId, _vgaCurZoneNum);
+ debugN("%.5d %.5X: %5d %4d ", _vgaTickCounter, (unsigned int)(_vcPtr - _curVgaFile1), _vgaCurSpriteId, _vgaCurZoneNum);
dumpVideoScript(_vcPtr, true);
}
}
@@ -383,7 +383,7 @@ void AGOSEngine::vcSkipNextInstruction() {
}
if (_dumpVgaOpcodes)
- printf("; skipped\n");
+ debugN("; skipped\n");
}
// VGA Script commands
diff --git a/engines/agos/vga_ff.cpp b/engines/agos/vga_ff.cpp
index 0a6458ac11..38a3479292 100644
--- a/engines/agos/vga_ff.cpp
+++ b/engines/agos/vga_ff.cpp
@@ -405,9 +405,9 @@ void AGOSEngine_PuzzlePack::vc63_fastFadeIn() {
if (getBitFlag(100)) {
startOverlayAnims();
} else if (getBitFlag(103)) {
- printf("NameAndTime\n");
+ debug("vc63_fastFadeIn: NameAndTime");
} else if (getBitFlag(104)) {
- printf("HiScoreTable\n");
+ debug("vc63_fastFadeIn: HiScoreTable");
}
}
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp
index 747c9221ee..14b8de9e39 100644
--- a/engines/cine/anim.cpp
+++ b/engines/cine/anim.cpp
@@ -28,7 +28,7 @@
*/
#include "common/endian.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "cine/cine.h"
#include "cine/anim.h"
@@ -485,7 +485,7 @@ void convert4BBP(byte *dest, const byte *source, int16 width, int16 height) {
* @param[out] animHeader Image header reference
* @param readS Input stream open for reading
*/
-void loadAnimHeader(AnimHeaderStruct &animHeader, Common::MemoryReadStream readS) {
+void loadAnimHeader(AnimHeaderStruct &animHeader, Common::SeekableReadStream &readS) {
animHeader.field_0 = readS.readByte();
animHeader.field_1 = readS.readByte();
animHeader.field_2 = readS.readByte();
diff --git a/engines/cine/bg.cpp b/engines/cine/bg.cpp
index 08722c42e5..449e254021 100644
--- a/engines/cine/bg.cpp
+++ b/engines/cine/bg.cpp
@@ -25,7 +25,7 @@
#include "common/endian.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "cine/cine.h"
#include "cine/various.h"
diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp
index 5c2119c1e4..38a0eda13e 100644
--- a/engines/cine/cine.cpp
+++ b/engines/cine/cine.cpp
@@ -56,6 +56,7 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng
DebugMan.addDebugChannel(kCineDebugScript, "Script", "Script debug level");
DebugMan.addDebugChannel(kCineDebugPart, "Part", "Part debug level");
DebugMan.addDebugChannel(kCineDebugSound, "Sound", "Sound debug level");
+ _console = new CineConsole(this);
// Setup mixer
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
@@ -74,7 +75,9 @@ CineEngine::~CineEngine() {
if (getGameType() == Cine::GType_OS) {
freeErrmessDat();
}
+
DebugMan.clearAllDebugChannels();
+ delete _console;
}
Common::Error CineEngine::run() {
diff --git a/engines/cine/cine.h b/engines/cine/cine.h
index 114d98d442..dd00d9b206 100644
--- a/engines/cine/cine.h
+++ b/engines/cine/cine.h
@@ -49,6 +49,7 @@
#include "cine/anim.h"
#include "cine/bg_list.h"
#include "cine/various.h"
+#include "cine/console.h"
//#define DUMP_SCRIPTS
@@ -71,7 +72,7 @@
* yet been finished. The game is not completable.
*
*
- * Supported games:
+ * Games using this engine:
*
* Cinematique evo.1
* - Future Wars
@@ -99,6 +100,8 @@ struct SeqListElement;
typedef Common::HashMap<Common::String, const char *> StringPtrHashMap;
+class CineConsole;
+
class CineEngine : public Engine {
protected:
@@ -137,6 +140,8 @@ public:
StringPtrHashMap _volumeEntriesMap;
TextHandler _textHandler;
+ GUI::Debugger *getDebugger() { return _console; }
+
bool _restartRequested;
private:
@@ -151,6 +156,7 @@ private:
void mainLoop(int bootScriptIdx);
void readVolCnf();
+ CineConsole *_console;
bool _preLoad;
int _timerDelayMultiplier;
diff --git a/engines/gob/helper.h b/engines/cine/console.cpp
index d1f24792a5..87633a2644 100644
--- a/engines/gob/helper.h
+++ b/engines/cine/console.cpp
@@ -23,18 +23,21 @@
*
*/
-#ifndef GOB_HELPER_H
-#define GOB_HELPER_H
+#include "cine/console.h"
+#include "cine/cine.h"
-namespace Gob {
+namespace Cine {
-/** A strncpy that forces the final \0. */
-inline char *strncpy0(char *dest, const char *src, size_t n) {
- strncpy(dest, src, n);
- dest[n] = 0;
- return dest;
+CineConsole::CineConsole(CineEngine *vm) : GUI::Debugger(), _vm(vm) {
}
-} // End of namespace Gob
+CineConsole::~CineConsole() {
+}
+
+void CineConsole::preEnter() {
+}
+
+void CineConsole::postEnter() {
+}
-#endif // GOB_HELPER_H
+} // End of namespace Cine
diff --git a/engines/cine/console.h b/engines/cine/console.h
new file mode 100644
index 0000000000..256ec16ce6
--- /dev/null
+++ b/engines/cine/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef CINE_CONSOLE_H
+#define CINE_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Cine {
+
+class CineEngine;
+
+class CineConsole : public GUI::Debugger {
+public:
+ CineConsole(CineEngine *vm);
+ virtual ~CineConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ CineEngine *_vm;
+};
+
+} // End of namespace Cine
+
+#endif
diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index 3d280c20ef..644744d3ed 100644
--- a/engines/cine/main_loop.cpp
+++ b/engines/cine/main_loop.cpp
@@ -161,6 +161,12 @@ static void processEvent(Common::Event &event) {
case Common::KEYCODE_KP3:
moveUsingKeyboard(+1, -1); // Down & Right
break;
+ case Common::KEYCODE_d:
+ if (event.kbd.hasFlags(Common::KBD_CTRL)) {
+ g_cine->getDebugger()->attach();
+ g_cine->getDebugger()->onFrame();
+ }
+ // No Break to allow fallthrough to process 'd' without CTRL
default:
lastKeyStroke = event.kbd.keycode;
break;
diff --git a/engines/cine/module.mk b/engines/cine/module.mk
index 2260524dcb..6e77449c59 100644
--- a/engines/cine/module.mk
+++ b/engines/cine/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
anim.o \
bg.o \
bg_list.o \
+ console.o \
cine.o \
detection.o \
gfx.o \
diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp
index 82dc0a6ef1..2b456ad49d 100644
--- a/engines/cine/object.cpp
+++ b/engines/cine/object.cpp
@@ -25,7 +25,7 @@
#include "common/endian.h"
-#include "common/scummsys.h"
+#include "common/memstream.h"
#include "common/util.h"
#include "cine/cine.h"
diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp
index 0f7c50b7e5..fd426be66a 100644
--- a/engines/cine/saveload.cpp
+++ b/engines/cine/saveload.cpp
@@ -801,7 +801,7 @@ bool CineEngine::makeLoad(char *saveName) {
// Hopefully devices with more limited memory can also cope with this memory allocation.
saveSize = 256 * 1024;
}
- Common::SharedPtr<Common::MemoryReadStream> in(saveFile->readStream(saveSize));
+ Common::SharedPtr<Common::SeekableReadStream> in(saveFile->readStream(saveSize));
// Try to detect the used savegame format
enum CineSaveGameFormat saveGameFormat = detectSaveGameFormat(*in);
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index 430a32ac69..5db9a83928 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -3098,7 +3098,7 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx)
}
void dumpScript(char *dumpName) {
- Common::DumpFile fHandle;
+ Common::DumpFile fHandle;
uint16 i;
fHandle.open(dumpName);
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index dd4b4d4ab4..7ba084128b 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -25,7 +25,7 @@
#include "common/endian.h"
#include "common/file.h"
-#include "common/system.h"
+#include "common/memstream.h"
#include "cine/cine.h"
#include "cine/sound.h"
diff --git a/engines/cruise/background.cpp b/engines/cruise/background.cpp
index 3ac57cc376..edd52d3b4a 100644
--- a/engines/cruise/background.cpp
+++ b/engines/cruise/background.cpp
@@ -216,8 +216,7 @@ int loadBackground(const char *name, int idx) {
if (strlen(name) >= sizeof(backgroundTable[idx].name))
warning("background name length exceeded allowable maximum");
- strncpy(backgroundTable[idx].name, name, sizeof(backgroundTable[idx].name));
- backgroundTable[idx].name[sizeof(backgroundTable[idx].name) - 1] = 0;
+ Common::strlcpy(backgroundTable[idx].name, name, sizeof(backgroundTable[idx].name));
}
return (0);
diff --git a/engines/cruise/cell.cpp b/engines/cruise/cell.cpp
index 65cc234bda..19189721e3 100644
--- a/engines/cruise/cell.cpp
+++ b/engines/cruise/cell.cpp
@@ -65,9 +65,7 @@ cellStruct *addCell(cellStruct *pHead, int16 overlayIdx, int16 objIdx, int16 typ
if (currentHead2->type != 5) {
int16 lvar2;
- getSingleObjectParam(currentHead2->overlay, currentHead2->idx, 2, &lvar2);
-
- if (lvar2 >= var)
+ if (getSingleObjectParam(currentHead2->overlay, currentHead2->idx, 2, &lvar2) >= 0 && lvar2 >= var)
break;
}
diff --git a/engines/cruise/cruise.cpp b/engines/cruise/cruise.cpp
index 3af77f3ef3..8cc45202e7 100644
--- a/engines/cruise/cruise.cpp
+++ b/engines/cruise/cruise.cpp
@@ -101,7 +101,6 @@ Common::Error CruiseEngine::run() {
Cruise::changeCursor(Cruise::CURSOR_NORMAL);
CursorMan.showMouse(true);
-
lastTick = 0;
lastTickDebug = 0;
@@ -130,6 +129,7 @@ void CruiseEngine::initialize() {
// another bit of video init
readVolCnf();
+ _vm->_polyStruct = NULL;
}
void CruiseEngine::deinitialise() {
diff --git a/engines/cruise/cruise.h b/engines/cruise/cruise.h
index 94f5c68ca0..ad3bb20ca1 100644
--- a/engines/cruise/cruise.h
+++ b/engines/cruise/cruise.h
@@ -42,7 +42,7 @@
*
* Status of this engine: Game is completable, engine needs objectifying
*
- * Supported games:
+ * Games using this engine:
* - Cruise for a Corpse
*/
namespace Cruise {
diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp
index aa78f84e3d..6b487fadc9 100644
--- a/engines/cruise/cruise_main.cpp
+++ b/engines/cruise/cruise_main.cpp
@@ -44,11 +44,11 @@ gfxEntryStruct* linkedMsgList = NULL;
void MemoryList() {
if (!_vm->_memList.empty()) {
- printf("Current list of un-freed memory blocks:\n");
+ debug("Current list of un-freed memory blocks:");
Common::List<byte *>::iterator i;
for (i = _vm->_memList.begin(); i != _vm->_memList.end(); ++i) {
byte *v = *i;
- printf("%s - %d\n", (const char *)(v - 68), *((int32 *)(v - 72)));
+ debug("%s - %d", (const char *)(v - 68), *((int32 *)(v - 72)));
}
}
}
@@ -1886,11 +1886,27 @@ void CruiseEngine::mainLoop() {
currentMouseButton = 0;
}
- manageScripts(&relHead);
- manageScripts(&procHead);
-
- removeFinishedScripts(&relHead);
- removeFinishedScripts(&procHead);
+ // FIXME: I suspect that the original game does multiple script executions between game frames; the bug with
+ // Raoul appearing when looking at the book is being there are 3 script iterations separation between the
+ // scene being changed to the book, and the Raoul actor being frozen/disabled. This loop is a hack to ensure
+ // that when a background changes, a few extra script executions are done
+ bool bgChanged;
+ int numIterations = 1;
+
+ while (numIterations-- > 0) {
+ bgChanged = backgroundChanged[masterScreen];
+
+ manageScripts(&relHead);
+ manageScripts(&procHead);
+
+ removeFinishedScripts(&relHead);
+ removeFinishedScripts(&procHead);
+
+ if (!bgChanged && backgroundChanged[masterScreen]) {
+ bgChanged = true;
+ numIterations += 2;
+ }
+ }
processAnimation();
diff --git a/engines/cruise/dataLoader.cpp b/engines/cruise/dataLoader.cpp
index b2ac7a2fd6..03a2117f07 100644
--- a/engines/cruise/dataLoader.cpp
+++ b/engines/cruise/dataLoader.cpp
@@ -25,6 +25,7 @@
#include "cruise/cruise_main.h"
#include "common/endian.h"
+#include "common/memstream.h"
namespace Cruise {
@@ -55,7 +56,7 @@ void decodeGfxUnified(dataFileEntry *pCurrentFileEntry, int16 format) {
break;
default:
- error("Unkown gfx format %d", format);
+ error("Unknown gfx format %d", format);
}
uint8 *buffer = (uint8 *)MemAlloc(spriteSize);
diff --git a/engines/cruise/decompiler.cpp b/engines/cruise/decompiler.cpp
index 31d9dcef9b..3298776d0e 100644
--- a/engines/cruise/decompiler.cpp
+++ b/engines/cruise/decompiler.cpp
@@ -62,7 +62,7 @@ unsigned long int currentOffset;
unsigned long int dumpIdx = 0;
-FILE *fHandle = NULL;
+FILE *fHandle = NULL; // FIXME: Use Common::DumpFile instead of FILE
#define DECOMPILER_STACK_DEPTH 100
#define DECOMPILER_STACK_ENTRY_SIZE 5000
@@ -409,7 +409,7 @@ int decompLoadVar() {
return (0);
}
default: {
- printf("Unsupported type %d in opcodeType0\n",
+ debug("Unsupported type %d in opcodeType0",
currentScriptOpcodeType);
failed = 1;
}
@@ -483,7 +483,7 @@ int decompSaveVar() {
break;
}
default: {
- printf("Unsupported type %d in opcodeType1\n",
+ debug("Unsupported type %d in opcodeType1",
currentScriptOpcodeType);
failed = 1;
}
@@ -521,14 +521,14 @@ int decompOpcodeType2() {
pushDecomp("freeString[%d][%s]", short1,
decompSaveOpcodeVar);
} else {
- printf("Unsupported type %d in opcodeType2\n",
+ debug("Unsupported type %d in opcodeType2",
byte1 & 7);
failed = 1;
}
break;
}
default: {
- printf("Unsupported type %d in opcodeType2\n",
+ debug("Unsupported type %d in opcodeType2",
currentScriptOpcodeType);
failed = 1;
}
@@ -1264,7 +1264,7 @@ int decompFunction() {
}
default: {
addDecomp("OP_%X", currentScriptOpcodeType);
- printf("OPCODE: %X\n", currentScriptOpcodeType);
+ debug("OPCODE: %X", currentScriptOpcodeType);
failed = 1;
break;
}
@@ -1354,7 +1354,7 @@ void dumpScript(uint8 *ovlName, ovlDataStruct *ovlData, int idx) {
resolveVarName("0", 0x20, temp, scriptName);
- printf("decompiling script %d - %s\n", idx, scriptName);
+ debug("decompiling script %d - %s", idx, scriptName);
// return;
@@ -1394,17 +1394,17 @@ void dumpScript(uint8 *ovlName, ovlDataStruct *ovlData, int idx) {
currentScriptOpcodeType = opcodeType & 7;
if (!decompOpcodeTypeTable[(opcodeType & 0xFB) >> 3]) {
- printf("Unsupported opcode type %d in decomp\n",
+ debug("Unsupported opcode type %d in decomp",
(opcodeType & 0xFB) >> 3);
return;
}
- //printf("Optype: %d\n",(opcodeType&0xFB)>>3);
+ //debug("Optype: %d",(opcodeType&0xFB)>>3);
decompOpcodeTypeTable[(opcodeType & 0xFB) >> 3]();
if (failed) {
- printf("Aborting decompilation..\n");
+ debug("Aborting decompilation..");
fclose(fHandle);
return;
}
diff --git a/engines/cruise/function.cpp b/engines/cruise/function.cpp
index 3d07abf441..0b25ee59c1 100644
--- a/engines/cruise/function.cpp
+++ b/engines/cruise/function.cpp
@@ -58,17 +58,15 @@ int16 Op_LoadOverlay() {
updateAllScriptsImports();
- strcpy(nextOverlay, overlayName);
+ Common::strlcpy(nextOverlay, overlayName, sizeof(nextOverlay));
- return(overlayLoadResult);
+ return overlayLoadResult;
}
int16 Op_Strcpy() {
char *ptr1 = (char *)popPtr();
char *ptr2 = (char *)popPtr();
- //printf("strcpy %s\n",ptr1);
-
while (*ptr1) {
*ptr2 = *ptr1;
@@ -1994,7 +1992,6 @@ int32 opcodeType8() {
return (-21);
if (opcode < ARRAYSIZE(opcodeTablePtr) && opcodeTablePtr[opcode]) {
- // printf("Function: %d\n",opcode);
pushVar(opcodeTablePtr[opcode]());
return (0);
} else {
diff --git a/engines/cruise/object.cpp b/engines/cruise/object.cpp
index 4d2c1c2273..864491605f 100644
--- a/engines/cruise/object.cpp
+++ b/engines/cruise/object.cpp
@@ -79,6 +79,12 @@ int16 getMultipleObjectParam(int16 overlayIdx, int16 objectIdx, objectParamsQuer
state = globalVars[overlayTable[overlayIdx].state + ptr->_stateTableIdx];
ptr2 = &ovlData->arrayStates[ptr->_firstStateIdx + state];
+
+ if (ptr->_firstStateIdx + state < 0) {
+ debug(0, "Invalid Negative arrayState index in getMultipleObjectParam(overlayIdx: %d, objectIdx: %d)... Forcing to 0", overlayIdx, objectIdx);
+ ptr2 = &ovlData->arrayStates[0];
+ }
+
state2 = ptr2->state;
break;
}
@@ -242,6 +248,11 @@ int16 getSingleObjectParam(int16 overlayIdx, int16 param2, int16 param3, int16 *
state = globalVars[overlayTable[overlayIdx].state + ptr->_stateTableIdx];
ptr2 = &ovlData->arrayStates[ptr->_firstStateIdx + state];
+
+ if (ptr->_firstStateIdx + state < 0) {
+ debug(0, "Invalid Negative arrayState index in getSingleObjectParam(overlayIdx: %d, param2: %d, param3: %d)... Forcing to 0", overlayIdx, param2, param3);
+ ptr2 = &ovlData->arrayStates[0];
+ }
break;
}
case VARIABLE: {
diff --git a/engines/cruise/overlay.cpp b/engines/cruise/overlay.cpp
index 729734d261..9a77891deb 100644
--- a/engines/cruise/overlay.cpp
+++ b/engines/cruise/overlay.cpp
@@ -23,7 +23,7 @@
*
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "cruise/cruise.h"
#include "cruise/cruise_main.h"
@@ -662,6 +662,7 @@ int loadOverlay(const char *scriptName) {
#endif
#ifdef DUMP_OBJECT
{
+ // TODO: Rewrite this to use Common::DumpFile
int i;
FILE *fHandle;
char nameBundle[100];
diff --git a/engines/cruise/saveload.cpp b/engines/cruise/saveload.cpp
index 24ea2facfe..a1a306705e 100644
--- a/engines/cruise/saveload.cpp
+++ b/engines/cruise/saveload.cpp
@@ -901,8 +901,7 @@ Common::Error loadSavegameData(int saveGameIdx) {
}
/*if (j < 2) {
- printf("Unsupported mono file load!\n");
- ASSERT(0);
+ error("Unsupported mono file load");
//loadFileMode1(filesDatabase[j].subData.name,filesDatabase[j].subData.var4);
} else */
if (strlen(filesDatabase[i].subData.name) > 0) {
diff --git a/engines/cruise/script.cpp b/engines/cruise/script.cpp
index d6c1aa47f3..317dcfbbe6 100644
--- a/engines/cruise/script.cpp
+++ b/engines/cruise/script.cpp
@@ -185,8 +185,9 @@ int32 opcodeType1() {
return 0;
}
case 2: {
+ assert (ptr);
*(ptr + var_A + offset) = var;
- return (0);
+ return 0;
}
default:
error("Unsupported code in opcodeType1 case 1");
@@ -448,7 +449,7 @@ int32 opcodeType3() { // math
}
int32 opcodeType9() { // stop script
- //printf("Stop a script of overlay %s\n",overlayTable[currentScriptPtr->overlayNumber].overlayName);
+ //debug("Stop a script of overlay %s", overlayTable[currentScriptPtr->overlayNumber].overlayName);
currentScriptPtr->scriptNumber = -1;
return (1);
}
@@ -500,7 +501,7 @@ uint8 *attacheNewScriptToTail(scriptInstanceStruct *scriptHandlePtr, int16 overl
int var_C;
scriptInstanceStruct *oldTail;
- //printf("Starting script %d of overlay %s\n",param,overlayTable[overlayNumber].overlayName);
+ //debug("Starting script %d of overlay %s", param,overlayTable[overlayNumber].overlayName);
if (scriptType < 0) {
useArg3Neg = 1;
diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp
index 84547237cc..3cb499c6a4 100644
--- a/engines/cruise/sound.cpp
+++ b/engines/cruise/sound.cpp
@@ -40,6 +40,7 @@ class PCSoundDriver {
public:
typedef void (*UpdateCallback)(void *);
+ PCSoundDriver() { _upCb = NULL, _upRef = NULL, _musicVolume = 0, _sfxVolume = 0; }
virtual ~PCSoundDriver() {}
virtual void setupChannel(int channel, const byte *data, int instrument, int volume) = 0;
diff --git a/engines/cruise/vars.cpp b/engines/cruise/vars.cpp
index c61cedc503..07bd646cae 100644
--- a/engines/cruise/vars.cpp
+++ b/engines/cruise/vars.cpp
@@ -51,8 +51,8 @@ int32 volumeDataLoaded = 0;
int16 numOfDisks;
-char lastOverlay[15];
-char nextOverlay[15];
+char lastOverlay[38];
+char nextOverlay[38];
int16 currentActiveMenu;
int16 autoMsg;
diff --git a/engines/cruise/vars.h b/engines/cruise/vars.h
index af39993f2f..54920a1436 100644
--- a/engines/cruise/vars.h
+++ b/engines/cruise/vars.h
@@ -154,8 +154,8 @@ extern int32 volumeDataLoaded;
extern int16 numOfDisks;
-extern char lastOverlay[15];
-extern char nextOverlay[15];
+extern char lastOverlay[38];
+extern char nextOverlay[38];
extern int16 currentActiveMenu;
extern int16 autoMsg;
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index cb081e4683..0d70be1ccd 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -33,9 +33,10 @@
#include "graphics/scaler.h"
#include "gui/about.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
#include "gui/launcher.h"
-#include "gui/ListWidget.h"
+#include "gui/widgets/list.h"
+#include "gui/message.h"
#include "gui/options.h"
#include "gui/saveload.h"
#include "gui/ThemeEval.h"
@@ -48,9 +49,6 @@
#include "gui/KeysDialog.h"
#endif
-using GUI::CommandSender;
-using GUI::StaticTextWidget;
-
class ConfigDialog : public GUI::OptionsDialog {
protected:
#ifdef SMALL_SCREEN_DEVICE
@@ -75,15 +73,15 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
_logo->useThemeTransparency(true);
_logo->setGfx(g_gui.theme()->getImageSurface(GUI::ThemeEngine::kImageLogoSmall));
} else {
- StaticTextWidget *title = new StaticTextWidget(this, "GlobalMenu.Title", "ScummVM");
+ GUI::StaticTextWidget *title = new GUI::StaticTextWidget(this, "GlobalMenu.Title", "ScummVM");
title->setAlign(Graphics::kTextAlignCenter);
}
#else
- StaticTextWidget *title = new StaticTextWidget(this, "GlobalMenu.Title", "ScummVM");
+ GUI::StaticTextWidget *title = new GUI::StaticTextWidget(this, "GlobalMenu.Title", "ScummVM");
title->setAlign(Graphics::kTextAlignCenter);
#endif
- StaticTextWidget *version = new StaticTextWidget(this, "GlobalMenu.Version", gScummVMVersionDate);
+ GUI::StaticTextWidget *version = new GUI::StaticTextWidget(this, "GlobalMenu.Version", gScummVMVersionDate);
version->setAlign(Graphics::kTextAlignCenter);
new GUI::ButtonWidget(this, "GlobalMenu.Resume", _("~R~esume"), 0, kPlayCmd, 'P');
@@ -102,7 +100,6 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
// To enable "Help", an engine needs to use a subclass of MainMenuDialog
// (at least for now, we might change how this works in the future).
_helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", _("~H~elp"), 0, kHelpCmd);
- _helpButton->setEnabled(false);
new GUI::ButtonWidget(this, "GlobalMenu.About", _("~A~bout"), 0, kAboutCmd);
@@ -130,7 +127,7 @@ MainMenuDialog::~MainMenuDialog() {
delete _saveDialog;
}
-void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+void MainMenuDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
switch (cmd) {
case kPlayCmd:
close();
@@ -147,8 +144,13 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
case kAboutCmd:
_aboutDialog->runModal();
break;
- case kHelpCmd:
- // Not handled here -- needs to be handled by a subclass (for now)
+ case kHelpCmd: {
+ GUI::MessageDialog dialog(
+ "Sorry, this engine does not currently provide in-game help. "
+ "Please consult the README for basic information, and for "
+ "instructions on how to obtain further assistance.");
+ dialog.runModal();
+ }
break;
case kRTLCmd: {
Common::Event eventRTL;
@@ -174,6 +176,15 @@ void MainMenuDialog::reflowLayout() {
_loadButton->setEnabled(_engine->canLoadGameStateCurrently());
if (_engine->hasFeature(Engine::kSupportsSavingDuringRuntime))
_saveButton->setEnabled(_engine->canSaveGameStateCurrently());
+
+ // Overlay size might have changed since the construction of the dialog.
+ // Update labels when it might be needed
+ // FIXME: it might be better to declare GUI::StaticTextWidget::setLabel() virtual
+ // and to reimplement it in GUI::ButtonWidget to handle the hotkey.
+ if (g_system->getOverlayWidth() > 320)
+ _rtlButton->setLabel(_rtlButton->cleanupHotkey(_("~R~eturn to Launcher")));
+ else
+ _rtlButton->setLabel(_rtlButton->cleanupHotkey(_c("~R~eturn to Launcher", "lowres")));
#ifndef DISABLE_FANCY_THEMES
if (g_gui.xmlEval()->getVar("Globals.ShowGlobalMenuLogo", 0) == 1 && g_gui.theme()->supportsImages()) {
@@ -182,16 +193,16 @@ void MainMenuDialog::reflowLayout() {
_logo->useThemeTransparency(true);
_logo->setGfx(g_gui.theme()->getImageSurface(GUI::ThemeEngine::kImageLogoSmall));
- GUI::StaticTextWidget *title = (StaticTextWidget *)findWidget("GlobalMenu.Title");
+ GUI::StaticTextWidget *title = (GUI::StaticTextWidget *)findWidget("GlobalMenu.Title");
if (title) {
removeWidget(title);
title->setNext(0);
delete title;
}
} else {
- GUI::StaticTextWidget *title = (StaticTextWidget *)findWidget("GlobalMenu.Title");
+ GUI::StaticTextWidget *title = (GUI::StaticTextWidget *)findWidget("GlobalMenu.Title");
if (!title) {
- title = new StaticTextWidget(this, "GlobalMenu.Title", "ScummVM");
+ title = new GUI::StaticTextWidget(this, "GlobalMenu.Title", "ScummVM");
title->setAlign(Graphics::kTextAlignCenter);
}
@@ -316,7 +327,7 @@ ConfigDialog::~ConfigDialog() {
#endif
}
-void ConfigDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+void ConfigDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
switch (cmd) {
case kKeysCmd:
diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp
index 2bedbe1801..d7582ec31d 100644
--- a/engines/draci/animation.cpp
+++ b/engines/draci/animation.cpp
@@ -31,6 +31,8 @@
#include "draci/sound.h"
#include "draci/surface.h"
+#include "common/memstream.h"
+
namespace Draci {
Animation::Animation(DraciEngine *vm, int id, uint z, bool playing) : _vm(vm) {
diff --git a/engines/draci/barchive.cpp b/engines/draci/barchive.cpp
index 8f9e836ba3..5307e04250 100644
--- a/engines/draci/barchive.cpp
+++ b/engines/draci/barchive.cpp
@@ -25,7 +25,7 @@
#include "common/debug.h"
#include "common/str.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "draci/barchive.h"
#include "draci/draci.h"
diff --git a/engines/draci/console.cpp b/engines/draci/console.cpp
new file mode 100644
index 0000000000..96e8e3ae05
--- /dev/null
+++ b/engines/draci/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "draci/console.h"
+#include "draci/draci.h"
+
+namespace Draci {
+
+DraciConsole::DraciConsole(DraciEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+DraciConsole::~DraciConsole() {
+}
+
+void DraciConsole::preEnter() {
+}
+
+void DraciConsole::postEnter() {
+}
+
+} // End of namespace Draci
diff --git a/engines/draci/console.h b/engines/draci/console.h
new file mode 100644
index 0000000000..811e14e0be
--- /dev/null
+++ b/engines/draci/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef DRACI_CONSOLE_H
+#define DRACI_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Draci {
+
+class DraciEngine;
+
+class DraciConsole : public GUI::Debugger {
+public:
+ DraciConsole(DraciEngine *vm);
+ virtual ~DraciConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ DraciEngine *_vm;
+};
+
+} // End of namespace Draci
+
+#endif
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index 07a9928cfa..78c5c71605 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -86,7 +86,7 @@ const ADGameDescription gameDescriptions[] = {
} // End of namespace Draci
-const ADParams detectionParams = {
+static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)Draci::gameDescriptions,
// Size of that superset structure
@@ -103,7 +103,7 @@ const ADParams detectionParams = {
0,
// Flags
0,
- // Global GUI options
+ // Additional GUI options (for every game}
Common::GUIO_NONE,
// Maximum directory depth
1,
@@ -202,10 +202,7 @@ SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int
int minutes = header.time & 0xFF;
desc.setSaveTime(hour, minutes);
- minutes = header.playtime / 60;
- hour = minutes / 60;
- minutes %= 60;
- desc.setPlayTime(hour, minutes);
+ desc.setPlayTime(header.playtime * 1000);
return desc;
}
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index 814159dbbb..d0eb511cbd 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -94,6 +94,8 @@ DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc)
DebugMan.addDebugChannel(kDraciSoundDebugLevel, "sound", "Sound debug info");
DebugMan.addDebugChannel(kDraciWalkingDebugLevel, "walking", "Walking debug info");
+ _console = new DraciConsole(this);
+
// Don't forget to register your random source
g_eventRec.registerRandomSource(_rnd, "draci");
}
@@ -346,6 +348,12 @@ void DraciEngine::handleEvents() {
_game->inventorySwitch(event.kbd.keycode);
}
break;
+ case Common::KEYCODE_d:
+ if (event.kbd.hasFlags(Common::KBD_CTRL)) {
+ this->getDebugger()->attach();
+ this->getDebugger()->onFrame();
+ }
+ break;
default:
break;
}
@@ -399,11 +407,13 @@ DraciEngine::~DraciEngine() {
// Remove all of our debug levels here
DebugMan.clearAllDebugChannels();
+
+ delete _console;
}
Common::Error DraciEngine::run() {
init();
- _engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime(0);
_game->init();
// Load game from specified slot, if any
@@ -418,8 +428,6 @@ Common::Error DraciEngine::run() {
void DraciEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
if (pause) {
- // Record start of the pause, so that we can later
- // adjust _engineStartTime accordingly.
_pauseStartTime = _system->getMillis();
_anims->pauseAnimations();
@@ -434,7 +442,6 @@ void DraciEngine::pauseEngineIntern(bool pause) {
// Adjust engine start time
const int delta = _system->getMillis() - _pauseStartTime;
- _engineStartTime += delta / 1000;
_game->shiftSpeechAndFadeTick(delta);
_pauseStartTime = 0;
}
diff --git a/engines/draci/draci.h b/engines/draci/draci.h
index 605e8cc238..aa6080ca05 100644
--- a/engines/draci/draci.h
+++ b/engines/draci/draci.h
@@ -28,6 +28,7 @@
#include "engines/engine.h"
#include "common/random.h"
+#include "draci/console.h"
struct ADGameDescription;
@@ -37,10 +38,10 @@ class OSystem;
/**
* This is the namespace of the Draci engine.
*
- * Status of this engine: ???
+ * Status of this engine: Complete
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Dragon History
*/
namespace Draci {
@@ -75,6 +76,8 @@ public:
virtual Common::Error saveGameState(int slot, const char *desc);
virtual bool canSaveGameStateCurrently();
+ GUI::Debugger *getDebugger() { return _console; }
+
Screen *_screen;
Mouse *_mouse;
Game *_game;
@@ -107,8 +110,9 @@ public:
Common::RandomSource _rnd;
- int32 _engineStartTime;
int32 _pauseStartTime;
+private:
+ DraciConsole *_console;
};
enum {
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index 8f3ad12cfd..eef81d808e 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -25,7 +25,7 @@
#include "common/keyboard.h"
#include "common/serializer.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "common/util.h"
@@ -207,7 +207,7 @@ void Game::init() {
_currentItem = _itemUnderCursor = NULL;
_previousItemPosition = -1;
-
+
_vm->_mouse->setCursorType(kHighlightedCursor); // anything different from kNormalCursor
_objUnderCursor = NULL;
@@ -1618,15 +1618,15 @@ int GameObject::addAnim(Animation *anim) {
}
void GameObject::playAnim(int i) {
- _anim[i]->play();
- _playingAnim = i;
+ _anim[i]->play();
+ _playingAnim = i;
}
void GameObject::stopAnim() {
- if (_playingAnim >= 0) {
- _anim[_playingAnim]->stop();
- _playingAnim = -1;
- }
+ if (_playingAnim >= 0) {
+ _anim[_playingAnim]->stop();
+ _playingAnim = -1;
+ }
}
void GameObject::deleteAnims() {
diff --git a/engines/draci/module.mk b/engines/draci/module.mk
index 1f80737135..e6c9511687 100644
--- a/engines/draci/module.mk
+++ b/engines/draci/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/draci
MODULE_OBJS := \
animation.o \
barchive.o \
+ console.o \
detection.o \
draci.o \
font.o \
diff --git a/engines/draci/mouse.cpp b/engines/draci/mouse.cpp
index 1d251d24c3..14d1162fed 100644
--- a/engines/draci/mouse.cpp
+++ b/engines/draci/mouse.cpp
@@ -54,10 +54,10 @@ void Mouse::handleEvent(Common::Event event) {
case Common::EVENT_LBUTTONUP:
debugC(6, kDraciGeneralDebugLevel, "Left button up (x: %u y: %u)", _x, _y);
- // Don't set _lButton to false, because some touchpads generate
- // down and up at such a quick succession, that they will
- // cancel each other in the same call of handleEvents(). Let
- // the game clear this flag by calling lButtonSet() instead.
+ // Don't set _lButton to false, because some touchpads generate
+ // down and up at such a quick succession, that they will
+ // cancel each other in the same call of handleEvents(). Let
+ // the game clear this flag by calling lButtonSet() instead.
break;
case Common::EVENT_RBUTTONDOWN:
diff --git a/engines/draci/saveload.cpp b/engines/draci/saveload.cpp
index 856e6da832..32e852d9a6 100644
--- a/engines/draci/saveload.cpp
+++ b/engines/draci/saveload.cpp
@@ -103,7 +103,7 @@ Common::Error saveSavegameData(int saveGameIdx, const Common::String &saveName,
header.saveName = saveName;
header.date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
header.time = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
- header.playtime = vm._system->getMillis() / 1000 - vm._engineStartTime;
+ header.playtime = vm.getTotalPlayTime() / 1000;
writeSavegameHeader(f, header);
if (f->err()) {
@@ -157,7 +157,7 @@ Common::Error loadSavegameData(int saveGameIdx, DraciEngine *vm) {
vm->_game->inventoryReload();
- vm->_engineStartTime = vm->_system->getMillis() / 1000 - header.playtime;
+ vm->setTotalPlayTime(header.playtime * 1000);
return Common::kNoError;
}
diff --git a/engines/draci/screen.cpp b/engines/draci/screen.cpp
index 9e91a14cfc..987bbf2ac1 100644
--- a/engines/draci/screen.cpp
+++ b/engines/draci/screen.cpp
@@ -23,7 +23,7 @@
*
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "draci/draci.h"
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index f657dfe33c..2a27541ad9 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -25,7 +25,7 @@
#include "common/array.h"
#include "common/debug.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/stack.h"
#include "draci/draci.h"
@@ -900,7 +900,7 @@ void Script::popNewRoom(const Common::Array<int> &params) {
* @brief Evaluates mathematical expressions
* @param reader Stream reader set to the beginning of the expression
*/
-int Script::handleMathExpression(Common::MemoryReadStream *reader) const {
+int Script::handleMathExpression(Common::ReadStream *reader) const {
Common::Stack<int> stk;
mathExpressionObject obj;
GPL2Operator oper;
diff --git a/engines/draci/script.h b/engines/draci/script.h
index 1551d3573c..e81691204e 100644
--- a/engines/draci/script.h
+++ b/engines/draci/script.h
@@ -30,7 +30,7 @@
#include "common/stream.h"
namespace Common {
- class MemoryReadStream;
+ class ReadStream;
}
namespace Draci {
@@ -193,7 +193,7 @@ private:
void setupCommandList();
const GPL2Command *findCommand(byte num, byte subnum) const;
- int handleMathExpression(Common::MemoryReadStream *reader) const;
+ int handleMathExpression(Common::ReadStream *reader) const;
DraciEngine *_vm;
};
diff --git a/engines/draci/sound.cpp b/engines/draci/sound.cpp
index c9244d7eac..af096256ea 100644
--- a/engines/draci/sound.cpp
+++ b/engines/draci/sound.cpp
@@ -28,7 +28,8 @@
#include "common/debug.h"
#include "common/file.h"
#include "common/str.h"
-#include "common/stream.h"
+#include "common/substream.h"
+#include "common/memstream.h"
#include "common/unzip.h"
#include "draci/sound.h"
@@ -301,7 +302,7 @@ uint Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffe
// only used for dubbing, which is only played from one place in
// script.cpp, which blocks until the dubbed sentence has finished
// playing.
- Common::SeekableReadStream* stream;
+ Common::SeekableReadStream *stream;
const int skip = buffer._format == RAW80 ? 80 : 0;
if (buffer._stream) {
stream = new Common::SeekableSubReadStream(
@@ -407,14 +408,14 @@ void Sound::stopVoice() {
}
void Sound::setVolume() {
- if (_mixer->isReady()) {
- _muteSound = ConfMan.getBool("sfx_mute");
- _muteVoice = ConfMan.getBool("speech_mute");
- } else {
- _muteSound = _muteVoice = true;
- }
+ if (_mixer->isReady()) {
+ _muteSound = ConfMan.getBool("sfx_mute");
+ _muteVoice = ConfMan.getBool("speech_mute");
+ } else {
+ _muteSound = _muteVoice = true;
+ }
if (ConfMan.getBool("mute")) {
- _muteSound = _muteVoice = true;
+ _muteSound = _muteVoice = true;
}
_showSubtitles = ConfMan.getBool("subtitles");
_talkSpeed = ConfMan.getInt("talkspeed");
diff --git a/engines/draci/sprite.cpp b/engines/draci/sprite.cpp
index cb3aa58cfa..65c6e21ee4 100644
--- a/engines/draci/sprite.cpp
+++ b/engines/draci/sprite.cpp
@@ -23,7 +23,7 @@
*
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "draci/draci.h"
#include "draci/font.h"
diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp
index 02612832d2..c6f9a58a85 100644
--- a/engines/draci/walking.cpp
+++ b/engines/draci/walking.cpp
@@ -23,9 +23,7 @@
*
*/
-#include <stdlib.h>
-
-#include "common/stream.h"
+#include "common/memstream.h"
#include "draci/draci.h"
#include "draci/animation.h"
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index 0dafcdc3cd..96de750c9c 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -88,10 +88,17 @@ static const DrasculaGameDescription gameDescriptions[] = {
{
"drascula",
0,
- AD_ENTRY1s("packet.001", "c6a8697396e213a18472542d5f547cb4", 32847563),
+ {
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ // HACK: List packet.001 twice to ensure this detector entry
+ // is ranked just as high as the others (which each have two
+ // detection files).
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {NULL, 0, NULL, 0}
+ },
Common::EN_ANY,
Common::kPlatformPC,
- ADGF_KEEPMATCH | GF_PACKED,
+ GF_PACKED,
GUIO_NONE
},
},
@@ -281,7 +288,7 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOMIDI,
+ Common::GUIO_NOMIDI | Common::GUIO_NOLAUNCHLOAD,
// Maximum directory depth
1,
// List of directory globs
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index 7c87f3574d..8bf190ed2d 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -30,6 +30,8 @@
#include "common/savefile.h"
#include "common/config-manager.h"
+#include "backends/audiocd/audiocd.h"
+
#include "base/plugins.h"
#include "base/version.h"
diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h
index 0a8b7c8c9b..535a9005d2 100644
--- a/engines/drascula/drascula.h
+++ b/engines/drascula/drascula.h
@@ -47,8 +47,8 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Drascula: The Vampire Strikes Back
*/
namespace Drascula {
diff --git a/engines/drascula/resource.cpp b/engines/drascula/resource.cpp
index 1226bc2e10..57be51da43 100644
--- a/engines/drascula/resource.cpp
+++ b/engines/drascula/resource.cpp
@@ -92,13 +92,19 @@ void TextResourceParser::getLine(char *buf) {
void TextResourceParser::parseInt(int &result) {
char buf[256];
getLine(buf);
- sscanf(buf, "%d", &result);
+
+ if (!sscanf(buf, "%d", &result)) {
+ result = 0;
+ }
}
void TextResourceParser::parseString(char* result) {
char buf[256];
getLine(buf);
- sscanf(buf, "%s", result);
+
+ if (!sscanf(buf, "%s", result)) {
+ *result = 0;
+ }
}
diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp
index 57bfad26af..c6dd9f29db 100644
--- a/engines/drascula/rooms.cpp
+++ b/engines/drascula/rooms.cpp
@@ -1945,7 +1945,9 @@ bool DrasculaEngine::exitRoom(int doorNumber) {
hare_se_ve = 1;
clearRoom();
- sscanf(_targetSurface[doorNumber], "%d", &roomNum);
+ if (!sscanf(_targetSurface[doorNumber], "%d", &roomNum)) {
+ error("Malformed roomNum in targetSurface (%s)", _targetSurface[doorNumber]);
+ }
curX = -1;
enterRoom(roomNum);
diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp
index 4aaec5ec0e..4c288553a2 100644
--- a/engines/drascula/saveload.cpp
+++ b/engines/drascula/saveload.cpp
@@ -221,7 +221,9 @@ bool DrasculaEngine::loadGame(const char *gameName) {
takeObject = sav->readSint32LE();
pickedObject = sav->readSint32LE();
loadedDifferentChapter = 0;
- sscanf(currentData, "%d.ald", &roomNum);
+ if (!sscanf(currentData, "%d.ald", &roomNum)) {
+ error("Bad save format");
+ }
enterRoom(roomNum);
selectVerb(kVerbNone);
diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp
index 2233115f3d..b73874ef80 100644
--- a/engines/drascula/sound.cpp
+++ b/engines/drascula/sound.cpp
@@ -30,6 +30,8 @@
#include "common/config-manager.h"
+#include "backends/audiocd/audiocd.h"
+
#include "drascula/drascula.h"
namespace Drascula {
diff --git a/engines/engine.cpp b/engines/engine.cpp
index 14715ccaac..b8ddc631df 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -46,7 +46,7 @@
#include "gui/debugger.h"
#include "gui/message.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
#include "sound/mixer.h"
@@ -94,10 +94,11 @@ Engine::Engine(OSystem *syst)
_saveFileMan(_system->getSavefileManager()),
_targetName(ConfMan.getActiveDomainName()),
_pauseLevel(0),
+ _pauseStartTime(0),
+ _engineStartTime(_system->getMillis()),
_mainMenuDialog(NULL) {
g_engine = this;
- Common::setDebugOutputFormatter(defaultOutputFormatter);
Common::setErrorOutputFormatter(defaultOutputFormatter);
Common::setErrorHandler(defaultErrorHandler);
@@ -154,7 +155,10 @@ void initCommonGFX(bool defaultTo1XScaler) {
// See if the game should default to 1x scaler
if (useDefaultGraphicsMode && defaultTo1XScaler) {
- g_system->resetGraphicsScale();
+ // FIXME: As a hack, we use "1x" here. Would be nicer to use
+ // getDefaultGraphicsMode() instead, but right now, we do not specify
+ // whether that is a 1x scaler or not...
+ g_system->setGraphicsMode("1x");
} else {
// Override global scaler with any game-specific define
if (ConfMan.hasKey("gfx_mode")) {
@@ -377,9 +381,12 @@ void Engine::pauseEngine(bool pause) {
_pauseLevel--;
if (_pauseLevel == 1 && pause) {
+ _pauseStartTime = _system->getMillis();
pauseEngineIntern(true);
} else if (_pauseLevel == 0) {
pauseEngineIntern(false);
+ _engineStartTime += _system->getMillis() - _pauseStartTime;
+ _pauseStartTime = 0;
}
}
@@ -395,6 +402,24 @@ void Engine::openMainMenuDialog() {
syncSoundSettings();
}
+uint32 Engine::getTotalPlayTime() const {
+ if (!_pauseLevel)
+ return _system->getMillis() - _engineStartTime;
+ else
+ return _pauseStartTime - _engineStartTime;
+}
+
+void Engine::setTotalPlayTime(uint32 time) {
+ const uint32 currentTime = _system->getMillis();
+
+ // We need to reset the pause start time here in case the engine is already
+ // paused to avoid any incorrect play time counting.
+ if (_pauseLevel > 0)
+ _pauseStartTime = currentTime;
+
+ _engineStartTime = currentTime - time;
+}
+
int Engine::runDialog(GUI::Dialog &dialog) {
pauseEngine(true);
int result = dialog.runModal();
diff --git a/engines/engine.h b/engines/engine.h
index ead1526d72..b4764319b8 100644
--- a/engines/engine.h
+++ b/engines/engine.h
@@ -74,6 +74,17 @@ private:
*/
int _pauseLevel;
+ /**
+ * The time when the pause was started.
+ */
+ uint32 _pauseStartTime;
+
+ /**
+ * The time when the engine was started. This value is used to calculate
+ * the current play time of the game running.
+ */
+ int32 _engineStartTime;
+
public:
@@ -234,6 +245,24 @@ public:
*/
void openMainMenuDialog();
+ /**
+ * Get the total play time.
+ *
+ * @return How long the player has been playing in ms.
+ */
+ uint32 getTotalPlayTime() const;
+
+ /**
+ * Set the game time counter to the specified time.
+ *
+ * This can be used to set the play time counter after loading a savegame
+ * for example. Another use case is in case the engine wants to exclude
+ * time from the counter the user spent in original engine dialogs.
+ *
+ * @param time Play time to set up in ms.
+ */
+ void setTotalPlayTime(uint32 time = 0);
+
inline Common::TimerManager *getTimerManager() { return _timer; }
inline Common::EventManager *getEventManager() { return _eventMan; }
inline Common::SaveFileManager *getSaveFileManager() { return _saveFileMan; }
diff --git a/engines/engines.mk b/engines/engines.mk
index be119c35d6..eea4ffc0b9 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -74,6 +74,11 @@ DEFINES += -DENABLE_LOL
endif
endif
+ifdef ENABLE_LASTEXPRESS
+DEFINES += -DENABLE_LASTEXPRESS=$(ENABLE_LASTEXPRESS)
+MODULES += engines/lastexpress
+endif
+
ifdef ENABLE_LURE
DEFINES += -DENABLE_LURE=$(ENABLE_LURE)
MODULES += engines/lure
diff --git a/engines/game.h b/engines/game.h
index b125421ff6..3e5d7f262c 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -28,7 +28,6 @@
#include "common/array.h"
#include "common/hash-str.h"
-#include "engines/savestate.h" // TODO: Push this #include out to .cpp files needing it
/**
* A simple structure used to map gameids (like "monkey", "sword1", ...) to
diff --git a/engines/gob/console.cpp b/engines/gob/console.cpp
new file mode 100644
index 0000000000..b6b481f611
--- /dev/null
+++ b/engines/gob/console.cpp
@@ -0,0 +1,163 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "gob/console.h"
+#include "gob/gob.h"
+#include "gob/inter.h"
+#include "gob/dataio.h"
+
+namespace Gob {
+
+GobConsole::GobConsole(GobEngine *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("varSize", WRAP_METHOD(GobConsole, cmd_varSize));
+ DCmd_Register("var8", WRAP_METHOD(GobConsole, cmd_var8));
+ DCmd_Register("var16", WRAP_METHOD(GobConsole, cmd_var16));
+ DCmd_Register("var32", WRAP_METHOD(GobConsole, cmd_var32));
+ DCmd_Register("varString", WRAP_METHOD(GobConsole, cmd_varString));
+ DCmd_Register("listArchives", WRAP_METHOD(GobConsole, cmd_listArchives));
+}
+
+GobConsole::~GobConsole() {
+}
+
+void GobConsole::preEnter() {
+}
+
+void GobConsole::postEnter() {
+}
+
+bool GobConsole::cmd_varSize(int argc, const char **argv) {
+ DebugPrintf("Size of the variable space: %d bytes\n", _vm->_inter->_variables->getSize());
+ return true;
+}
+
+bool GobConsole::cmd_var8(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: var8 <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if (varNum >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 varVal = atoi(argv[2]);
+ _vm->_inter->_variables->writeOff8(varNum, varVal);
+ }
+
+ DebugPrintf("var8_%d = %d\n", varNum, _vm->_inter->_variables->readOff8(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_var16(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: var16 <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if ((varNum + 1) >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 varVal = atoi(argv[2]);
+ _vm->_inter->_variables->writeOff16(varNum, varVal);
+ }
+
+ DebugPrintf("var16_%d = %d\n", varNum, _vm->_inter->_variables->readOff16(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_var32(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: var32 <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if ((varNum + 3) >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 varVal = atoi(argv[2]);
+ _vm->_inter->_variables->writeOff32(varNum, varVal);
+ }
+
+ DebugPrintf("var8_%d = %d\n", varNum, _vm->_inter->_variables->readOff32(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_varString(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: varString <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if (varNum >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 maxLength = _vm->_inter->_variables->getSize() - varNum;
+
+ Common::strlcpy(_vm->_inter->_variables->getAddressOffString(varNum), argv[2], maxLength);
+ }
+
+ DebugPrintf("varString_%d = \"%s\"\n", varNum, _vm->_inter->_variables->getAddressOffString(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_listArchives(int argc, const char **argv) {
+ Common::Array<ArchiveInfo> info;
+
+ _vm->_dataIO->getArchiveInfo(info);
+
+ DebugPrintf(" Archive | Base | FileCount\n");
+ DebugPrintf("--------------------------------\n");
+ for (Common::Array<ArchiveInfo>::const_iterator it = info.begin(); it != info.end(); ++it)
+ if (!it->name.empty())
+ DebugPrintf("%13s | %d | %d\n", it->name.c_str(), it->base, it->fileCount);
+
+ return true;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/console.h b/engines/gob/console.h
new file mode 100644
index 0000000000..f51d74be86
--- /dev/null
+++ b/engines/gob/console.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GOB_CONSOLE_H
+#define GOB_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Gob {
+
+class GobEngine;
+
+class GobConsole : public GUI::Debugger {
+public:
+ GobConsole(GobEngine *vm);
+ virtual ~GobConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ GobEngine *_vm;
+
+ bool cmd_varSize(int argc, const char **argv);
+ bool cmd_var8(int argc, const char **argv);
+ bool cmd_var16(int argc, const char **argv);
+ bool cmd_var32(int argc, const char **argv);
+ bool cmd_varString(int argc, const char **argv);
+
+ bool cmd_listArchives(int argc, const char **argv);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/gob/dataio.cpp b/engines/gob/dataio.cpp
index 694cec6a6f..b145441b4a 100644
--- a/engines/gob/dataio.cpp
+++ b/engines/gob/dataio.cpp
@@ -24,207 +24,114 @@
*/
#include "common/endian.h"
+#include "common/types.h"
+#include "common/memstream.h"
#include "gob/gob.h"
#include "gob/dataio.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
namespace Gob {
-DataStream::DataStream(DataIO &io, int16 handle, uint32 dSize, bool dispose) {
- _io = &io;
-
- _handle = handle;
- _size = dSize;
- _dispose = dispose;
-
- _data = 0;
- _stream = 0;
+DataIO::File::File() : size(0), offset(0), packed(false), archive(0) {
}
-DataStream::DataStream(byte *buf, uint32 dSize, bool dispose) {
- _data = buf;
- _size = dSize;
- _stream = new Common::MemoryReadStream(_data, _size);
- _dispose = dispose;
-
- _io = 0;
- _handle = -1;
+DataIO::File::File(const Common::String &n, uint32 s, uint32 o, bool p, Archive &a) :
+ name(n), size(s), offset(o), packed(p), archive(&a) {
}
-DataStream::~DataStream() {
- delete _stream;
- if (_dispose) {
- delete[] _data;
- if ((_handle >= 0) && _io)
- _io->closeData(_handle);
- }
+DataIO::DataIO() {
+ // Reserve memory for the standard max amount of archives
+ _archives.reserve(kMaxArchives);
+ for (int i = 0; i < kMaxArchives; i++)
+ _archives.push_back(0);
}
-int32 DataStream::pos() const {
- if (_stream)
- return _stream->pos();
-
- int32 resPos = _io->getChunkPos(_handle);
- if (resPos != -1)
- return resPos;
-
- return _io->file_getHandle(_handle)->pos();
-}
-
-int32 DataStream::size() const {
- if (_stream)
- return _stream->size();
-
- return _size;
-}
+DataIO::~DataIO() {
+ // Close all archives
+ for (Common::Array<Archive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+ if (!*it)
+ continue;
-bool DataStream::seek(int32 offset, int whence) {
- if (_stream)
- return _stream->seek(offset, whence);
- else if (!_io->isDataFileChunk(_handle))
- return _io->file_getHandle(_handle)->seek(offset, whence);
- else {
- _io->seekChunk(_handle, offset, whence);
- return true;
+ closeArchive(**it);
+ delete *it;
}
}
-bool DataStream::eos() const {
- if (_stream)
- return _stream->eos();
-
- return pos() >= size(); // FIXME (eos definition change)
-}
-
-uint32 DataStream::read(void *dataPtr, uint32 dataSize) {
- if (_stream)
- return _stream->read(dataPtr, dataSize);
-
- if (!_io->isDataFileChunk(_handle))
- return _io->file_getHandle(_handle)->read((byte *)dataPtr, dataSize);
-
- byte *data = (byte *)dataPtr;
- uint32 haveRead = 0;
- while (dataSize > 0x3FFF) {
- _io->readChunk(_handle, (byte *)data, 0x3FFF);
- dataSize -= 0x3FFF;
- data += 0x3FFF;
- haveRead += 0x3FFF;
- }
- _io->readChunk(_handle, (byte *)data, dataSize);
-
- return haveRead + dataSize;
-}
+void DataIO::getArchiveInfo(Common::Array<ArchiveInfo> &info) const {
+ info.resize(_archives.size());
-DataIO::DataIO(GobEngine *vm) : _vm(vm) {
- for (int i = 0; i < MAX_DATA_FILES; i++) {
- _dataFiles[i] = 0;
- _numDataChunks[i] = 0;
- _dataFileHandles[i] = -1;
- }
-}
+ for (uint i = 0; i < _archives.size(); i++) {
+ if (!_archives[i])
+ continue;
-DataIO::~DataIO() {
- for (int i = 0; i < MAX_DATA_FILES; i++) {
- if (_dataFiles[i])
- file_getHandle(_dataFileHandles[i])->close();
- delete[] _dataFiles[i];
+ info[i].name = _archives[i]->name;
+ info[i].base = _archives[i]->base;
+ info[i].fileCount = _archives[i]->files.size();
}
}
-bool DataIO::isDataFileChunk(int16 handle) const {
- return (handle >= 50) && (handle < 128);
-}
-
-bool DataIO::isPacked(int16 handle) const {
- if (!isDataFileChunk(handle))
- return false;
-
- return _chunk[getIndex(handle)]->packed != 0;
-}
-
-int DataIO::getFile(int16 handle) const {
- if (!isDataFileChunk(handle))
- return -1;
+byte *DataIO::unpack(const byte *src, uint32 srcSize, int32 &size) {
+ size = READ_LE_UINT32(src);
- return (handle - 50) / 10;
-}
+ byte *data = new byte[size];
-int DataIO::getSlot(int16 handle) const {
- if (!isDataFileChunk(handle))
- return -1;
-
- return (handle - 50) % 10;
+ Common::MemoryReadStream srcStream(src + 4, srcSize - 4);
+ unpack(srcStream, data, size);
+ return data;
}
-int DataIO::getIndex(int16 handle) const {
- if (!isDataFileChunk(handle))
- return -1;
-
- return getIndex(getFile(handle), getSlot(handle));
-}
+Common::SeekableReadStream *DataIO::unpack(Common::SeekableReadStream &src) {
+ uint32 size = src.readUint32LE();
-int DataIO::getIndex(int file, int slot) const {
- return file * MAX_SLOT_COUNT + slot;
-}
+ byte *data = (byte *) malloc(size);
-int16 DataIO::getHandle(int file, int slot) const {
- return file * 10 + slot + 50;
+ unpack(src, data, size);
+ return new Common::MemoryReadStream(data, size, DisposeAfterUse::YES);
}
-int32 DataIO::unpackData(byte *src, byte *dest) {
- uint32 realSize;
- uint32 counter;
- uint16 cmd;
- byte *tmpBuf;
- int16 off;
- byte len;
- uint16 tmpIndex;
-
- tmpBuf = new byte[4114];
+void DataIO::unpack(Common::SeekableReadStream &src, byte *dest, uint32 size) {
+ byte *tmpBuf = new byte[4114];
assert(tmpBuf);
- counter = realSize = READ_LE_UINT32(src);
+ uint32 counter = size;
for (int i = 0; i < 4078; i++)
tmpBuf[i] = 0x20;
- tmpIndex = 4078;
+ uint16 tmpIndex = 4078;
- src += 4;
-
- cmd = 0;
+ uint16 cmd = 0;
while (1) {
cmd >>= 1;
- if ((cmd & 0x0100) == 0) {
- cmd = *src | 0xFF00;
- src++;
- }
+ if ((cmd & 0x0100) == 0)
+ cmd = src.readByte() | 0xFF00;
+
if ((cmd & 1) != 0) { /* copy */
- *dest++ = *src;
- tmpBuf[tmpIndex] = *src;
- src++;
+ byte tmp = src.readByte();
+
+ *dest++ = tmp;
+ tmpBuf[tmpIndex] = tmp;
+
tmpIndex++;
tmpIndex %= 4096;
counter--;
if (counter == 0)
break;
} else { /* copy string */
+ byte tmp1 = src.readByte();
+ byte tmp2 = src.readByte();
- off = *src++;
- off |= (*src & 0xF0) << 4;
- len = (*src & 0x0F) + 3;
- src++;
+ int16 off = tmp1 | ((tmp2 & 0xF0) << 4);
+ byte len = (tmp2 & 0x0F) + 3;
for (int i = 0; i < len; i++) {
*dest++ = tmpBuf[(off + i) % 4096];
counter--;
if (counter == 0) {
delete[] tmpBuf;
- return realSize;
+ return;
}
tmpBuf[tmpIndex] = tmpBuf[(off + i) % 4096];
tmpIndex++;
@@ -233,390 +140,251 @@ int32 DataIO::unpackData(byte *src, byte *dest) {
}
}
- delete[] tmpBuf;
- return realSize;
-}
-
-Common::File *DataIO::file_getHandle(int16 handle) {
- return &_filesHandles[handle];
-}
-const Common::File *DataIO::file_getHandle(int16 handle) const {
- return &_filesHandles[handle];
+ delete[] tmpBuf;
}
-int16 DataIO::file_open(const char *path) {
- int16 i;
-
- for (i = 0; i < MAX_FILES; i++) {
- if (!file_getHandle(i)->isOpen())
+bool DataIO::openArchive(Common::String name, bool base) {
+ // Look for a free archive slot
+ Archive **archive = 0;
+ int i = 0;
+ for (Common::Array<Archive *>::iterator it = _archives.begin(); it != _archives.end(); ++it, i++) {
+ if (!*it) {
+ archive = &*it;
break;
+ }
}
- if (i == MAX_FILES)
- return -1;
-
- if (file_getHandle(i)->open(path))
- return i;
-
- return -1;
-}
-int16 DataIO::getChunk(const char *chunkName) {
- for (int16 file = 0; file < MAX_DATA_FILES; file++) {
- if (_dataFiles[file] == 0)
- return -1;
+ if (!archive) {
+ // No free slot, create a new one
- int16 slot;
- for (slot = 0; slot < MAX_SLOT_COUNT; slot++)
- if (_chunkPos[file * MAX_SLOT_COUNT + slot] == -1)
- break;
+ warning("DataIO::openArchive(): Need to increase archive count to %d", _archives.size() + 1);
+ _archives.push_back(0);
- if (slot == MAX_SLOT_COUNT) {
- warning("Chunk slots full");
- return -1;
- }
-
- ChunkDesc *dataDesc = _dataFiles[file];
- for (uint16 chunk = 0; chunk < _numDataChunks[file]; chunk++, dataDesc++) {
- if (scumm_stricmp(chunkName, dataDesc->chunkName) != 0)
- continue;
+ Common::Array<Archive *>::iterator it = _archives.end();
+ archive = &*(--it);
+ }
- int index = getIndex(file, slot);
+ // Add extension if necessary
+ if (!name.contains('.'))
+ name += ".stk";
- _isCurrentSlot[index] = false;
- _chunk [index] = dataDesc;
- _chunkPos [index] = 0;
+ // Try to open
+ *archive = openArchive(name);
+ if (!*archive)
+ return false;
- return getHandle(file, slot);
- }
- }
- return -1;
+ (*archive)->base = base;
+ return true;
}
-char DataIO::freeChunk(int16 handle) {
- if (isDataFileChunk(handle)) {
- _chunkPos[getIndex(handle)] = -1;
+DataIO::Archive *DataIO::openArchive(const Common::String &name) {
+ Archive *archive = new Archive;
+ if (!archive->file.open(name)) {
+ delete archive;
return 0;
}
- return 1;
-}
-
-int32 DataIO::readChunk(int16 handle, byte *buf, uint16 size) {
- if (!isDataFileChunk(handle))
- return -2;
-
- int file = getFile(handle);
- int slot = getSlot(handle);
- int index = getIndex(file, slot);
-
- _chunkPos[index] = CLIP<int32>(_chunkPos[index], 0, _chunk[index]->size);
-
- if (!_isCurrentSlot[index]) {
- for (int16 i = 0; i < MAX_SLOT_COUNT; i++)
- _isCurrentSlot[file * MAX_SLOT_COUNT + i] = false;
- int32 offset = _chunk[index]->offset + _chunkPos[index];
+ archive->name = name;
- debugC(7, kDebugFileIO, "seek: %d, %d", _chunk[index]->offset, _chunkPos[index]);
+ uint16 fileCount = archive->file.readUint16LE();
+ for (uint16 i = 0; i < fileCount; i++) {
+ File file;
- file_getHandle(_dataFileHandles[file])->seek(offset, SEEK_SET);
- }
-
- _isCurrentSlot[index] = true;
- if ((_chunkPos[index] + size) > (int32) (_chunk[index]->size))
- size = _chunk[index]->size - _chunkPos[index];
-
- file_getHandle(_dataFileHandles[file])->read(buf, size);
- _chunkPos[index] += size;
- return size;
-}
+ char fileName[14];
-int16 DataIO::seekChunk(int16 handle, int32 pos, int16 from) {
- if (!isDataFileChunk(handle))
- return -1;
+ archive->file.read(fileName, 13);
+ fileName[13] = '\0';
- int file = getFile(handle);
- int slot = getSlot(handle);
- int index = getIndex(file, slot);
+ file.size = archive->file.readUint32LE();
+ file.offset = archive->file.readUint32LE();
+ file.packed = archive->file.readByte() != 0;
- _isCurrentSlot[index] = false;
- if (from == SEEK_SET)
- _chunkPos[index] = pos;
- else if (from == SEEK_CUR)
- _chunkPos[index] += pos;
- else if (from == SEEK_END)
- _chunkPos[index] = _chunk[index]->size - pos;
+ // Replacing cyrillic characters
+ Util::replaceChar(fileName, (char) 0x85, 'E');
+ Util::replaceChar(fileName, (char) 0x8A, 'K');
+ Util::replaceChar(fileName, (char) 0x8E, 'O');
+ Util::replaceChar(fileName, (char) 0x91, 'C');
+ Util::replaceChar(fileName, (char) 0x92, 'T');
- return _chunkPos[index];
-}
+ file.name = fileName;
-uint32 DataIO::getChunkPos(int16 handle) const {
- if (!isDataFileChunk(handle))
- return 0xFFFFFFFF;
+ // Geisha use 0ot files, which are compressed TOT files without the packed byte set.
+ if (file.name.hasSuffix(".0OT")) {
+ file.name.setChar(file.name.size() - 3, 'T');
+ file.packed = true;
+ }
- int file = getFile(handle);
- int slot = getSlot(handle);
+ file.archive = archive;
+ archive->files.setVal(file.name, file);
+ }
- return _chunkPos[file * MAX_SLOT_COUNT + slot];
+ return archive;
}
-int32 DataIO::getChunkSize(const char *chunkName, int32 &packSize) {
- packSize = -1;
-
- for (int file = 0; file < MAX_DATA_FILES; file++) {
- if (_dataFiles[file] == 0)
- return -1;
-
- ChunkDesc *dataDesc = _dataFiles[file];
- for (uint16 chunk = 0; chunk < _numDataChunks[file]; chunk++, dataDesc++) {
- if (scumm_stricmp(chunkName, dataDesc->chunkName) != 0)
- continue;
-
- if (dataDesc->packed == 0)
- return dataDesc->size;
+bool DataIO::closeArchive(bool base) {
+ // Look for a matching archive and close it
+ for (int archive = _archives.size() - 1; archive >= 0; archive--) {
+ if (_archives[archive] && (_archives[archive]->base == base)) {
+ closeArchive(*_archives[archive]);
+ delete _archives[archive];
+ _archives[archive] = 0;
- for (int16 slot = 0; slot < MAX_SLOT_COUNT; slot++)
- _isCurrentSlot[slot] = false;
-
- int32 realSize;
-
- file_getHandle(_dataFileHandles[file])->seek(dataDesc->offset, SEEK_SET);
- realSize = file_getHandle(_dataFileHandles[file])->readUint32LE();
- packSize = dataDesc->size;
-
- return realSize;
+ return true;
}
}
- return -1;
-}
-
-void DataIO::openDataFile(const char *src, bool itk) {
- char path[128];
-
- strncpy0(path, src, 127);
- if (!strchr(path, '.')) {
- path[123] = 0;
- strcat(path, ".stk");
- }
-
- int16 file;
- for (file = 0; file < MAX_DATA_FILES; file++)
- if (_dataFiles[file] == 0)
- break;
- if (file == MAX_DATA_FILES)
- error("DataIO::openDataFile(): Data file slots are full");
-
- _dataFileHandles[file] = file_open(path);
+ return false;
+}
- if (_dataFileHandles[file] == -1)
- error("DataIO::openDataFile(): Can't open data file \"%s\"", path);
+bool DataIO::closeArchive(Archive &archive) {
+ archive.file.close();
- _dataFileItk [file] = itk;
- _numDataChunks[file] = file_getHandle(_dataFileHandles[file])->readUint16LE();
+ return true;
+}
- debugC(7, kDebugFileIO, "DataChunks: %d [for %s]", _numDataChunks[file], path);
+bool DataIO::hasFile(const Common::String &name){
+ // Look up the files in the opened archives
+ if (findFile(name))
+ return true;
- ChunkDesc *dataDesc = new ChunkDesc[_numDataChunks[file]];
- _dataFiles[file] = dataDesc;
+ // Else, look if a plain file that matches exists
+ return Common::File::exists(name);
+}
- for (int i = 0; i < _numDataChunks[file]; i++) {
- file_getHandle(_dataFileHandles[file])->read(dataDesc[i].chunkName, 13);
- dataDesc[i].size = file_getHandle(_dataFileHandles[file])->readUint32LE();
- dataDesc[i].offset = file_getHandle(_dataFileHandles[file])->readUint32LE();
- dataDesc[i].packed = file_getHandle(_dataFileHandles[file])->readByte();
+int32 DataIO::fileSize(const Common::String &name) {
+ // Try to find the file in the archives
+ File *file = findFile(name);
+ if (file) {
+ if (!file->packed)
+ return file->size;
- // Replacing cyrillic characters
- Util::replaceChar(dataDesc[i].chunkName, (char) 0x85, 'E');
- Util::replaceChar(dataDesc[i].chunkName, (char) 0x8A, 'K');
- Util::replaceChar(dataDesc[i].chunkName, (char) 0x8E, 'O');
- Util::replaceChar(dataDesc[i].chunkName, (char) 0x91, 'C');
- Util::replaceChar(dataDesc[i].chunkName, (char) 0x92, 'T');
+ // Sanity checks
+ assert(file->size >= 4);
+ assert(file->archive);
+ assert(file->archive->file.isOpen());
- // Geisha use 0ot files, which are compressed TOT files without the packed byte set.
- char *fakeTotPtr = strstr(dataDesc[i].chunkName, "0OT");
- if (fakeTotPtr != 0) {
- strncpy(fakeTotPtr, "TOT", 3);
- dataDesc[i].packed = 1;
- }
+ // Read the full, unpacked size
+ file->archive->file.seek(file->offset);
+ return file->archive->file.readUint32LE();
}
- for (int i = 0; i < _numDataChunks[file]; i++)
- debugC(7, kDebugFileIO, "%d: %s %d", i, dataDesc[i].chunkName, dataDesc[i].size);
+ // Else, try to find a matching plain file
+ Common::File f;
+ if (!f.open(name))
+ return -1;
- for (int i = 0; i < MAX_SLOT_COUNT; i++)
- _chunkPos[file * MAX_SLOT_COUNT + i] = -1;
+ return f.size();
}
-void DataIO::closeDataFile(bool itk) {
- for (int file = MAX_DATA_FILES - 1; file >= 0; file--) {
- if (_dataFiles[file] && (_dataFileItk[file] == itk)) {
- delete[] _dataFiles[file];
- _dataFiles[file] = 0;
- file_getHandle(_dataFileHandles[file])->close();
- return;
- }
+Common::SeekableReadStream *DataIO::getFile(const Common::String &name) {
+ // Try to open the file in the archives
+ File *file = findFile(name);
+ if (file) {
+ Common::SeekableReadStream *data = getFile(*file);
+ if (data)
+ return data;
}
-}
-
-byte *DataIO::getUnpackedData(const char *name) {
- int32 realSize;
- int32 packSize = -1;
-
- realSize = getChunkSize(name, packSize);
- if ((packSize == -1) || (realSize == -1))
+ // Else, try to open a matching plain file
+ Common::File f;
+ if (!f.open(name))
return 0;
- int16 chunk = getChunk(name);
- if (chunk == -1)
- return 0;
-
- byte *unpackBuf = new byte[realSize];
- assert(unpackBuf);
-
- byte *packBuf = new byte[packSize];
- assert(packBuf);
-
- int32 sizeLeft = packSize;
- byte *ptr = packBuf;
- while (sizeLeft > 0x4000) {
- readChunk(chunk, ptr, 0x4000);
- sizeLeft -= 0x4000;
- ptr += 0x4000;
- }
- readChunk(chunk, ptr, sizeLeft);
- freeChunk(chunk);
- unpackData(packBuf, unpackBuf);
-
- delete[] packBuf;
- return unpackBuf;
+ return f.readStream(f.size());
}
-void DataIO::closeData(int16 handle) {
- if (freeChunk(handle) != 0)
- file_getHandle(handle)->close();
-}
-
-int16 DataIO::openData(const char *path) {
- int16 handle = getChunk(path);
- if (handle >= 0)
- return handle;
-
- return file_open(path);
-}
-
-bool DataIO::existData(const char *path) {
- if (!path || (path[0] == '\0'))
- return false;
+byte *DataIO::getFile(const Common::String &name, int32 &size) {
+ // Try to open the file in the archives
+ File *file = findFile(name);
+ if (file) {
+ byte *data = getFile(*file, size);
+ if (data)
+ return data;
+ }
- int16 handle = openData(path);
- if (handle < 0)
- return false;
+ // Else, try to open a matching plain file
+ Common::File f;
+ if (!f.open(name))
+ return 0;
- closeData(handle);
- return true;
-}
+ size = f.size();
-DataStream *DataIO::openAsStream(int16 handle, bool dispose) {
- uint32 curPos = getPos(handle);
- seekData(handle, 0, SEEK_END);
- uint32 size = getPos(handle);
- seekData(handle, curPos, SEEK_SET);
+ byte *data = new byte[size];
+ if (f.read(data, size) != ((uint32) size)) {
+ delete[] data;
+ return 0;
+ }
- return new DataStream(*this, handle, size, dispose);
+ return 0;
}
-uint32 DataIO::getPos(int16 handle) {
- uint32 resPos = getChunkPos(handle);
- if (resPos != 0xFFFFFFFF)
- return resPos;
-
- return file_getHandle(handle)->pos();
-}
+DataIO::File *DataIO::findFile(const Common::String &name) {
+ for (int i = _archives.size() - 1; i >= 0; i--) {
+ Archive *archive = _archives[i];
+ if (!archive)
+ // Empty slot
+ continue;
-void DataIO::seekData(int16 handle, int32 pos, int16 from) {
- int32 resPos = seekChunk(handle, pos, from);
- if (resPos != -1)
- return;
+ // Look up the file in the file map
+ FileMap::iterator file = archive->files.find(name);
+ if (file != archive->files.end())
+ return &file->_value;
+ }
- file_getHandle(handle)->seek(pos, from);
+ return 0;
}
-int32 DataIO::readData(int16 handle, byte *buf, uint16 size) {
- int16 res = readChunk(handle, buf, size);
- if (res >= 0)
- return res;
+Common::SeekableReadStream *DataIO::getFile(File &file) {
+ if (!file.archive)
+ return 0;
- return file_getHandle(handle)->read(buf, size);
-}
+ if (!file.archive->file.isOpen())
+ return 0;
-int32 DataIO::getDataSize(const char *name) {
- char buf[128];
- int32 chunkSize;
- int32 packSize = -1;
+ if (!file.archive->file.seek(file.offset))
+ return 0;
- strncpy0(buf, name, 127);
+ Common::SeekableReadStream *rawData = file.archive->file.readStream(file.size);
+ if (!rawData)
+ return 0;
- chunkSize = getChunkSize(buf, packSize);
- if (chunkSize >= 0)
- return chunkSize;
+ if (!file.packed)
+ return rawData;
- Common::File file;
- if (!file.open(buf))
- error("DataIO::getDataSize(): Can't find data \"%s\"", name);
+ Common::SeekableReadStream *unpackedData = unpack(*rawData);
- chunkSize = file.size();
- file.close();
+ delete rawData;
- return chunkSize;
+ return unpackedData;
}
-byte *DataIO::getData(const char *path) {
- byte *data = getUnpackedData(path);
- if (data)
- return data;
-
- int32 size = getDataSize(path);
-
- data = new byte[size];
- assert(data);
+byte *DataIO::getFile(File &file, int32 &size) {
+ if (!file.archive)
+ return 0;
- int16 handle = openData(path);
+ if (!file.archive->file.isOpen())
+ return 0;
- byte *ptr = data;
- while (size > 0x4000) {
- readData(handle, ptr, 0x4000);
- size -= 0x4000;
- ptr += 0x4000;
- }
- readData(handle, ptr, size);
- closeData(handle);
- return data;
-}
+ if (!file.archive->file.seek(file.offset))
+ return 0;
-DataStream *DataIO::getDataStream(const char *path) {
- if (!existData(path))
- return 0;
+ size = file.size;
- int16 handle = openData(path);
- if (handle < 0)
+ byte *rawData = new byte[file.size];
+ if (file.archive->file.read(rawData, file.size) != file.size) {
+ delete[] rawData;
return 0;
+ }
- if (isDataFileChunk(handle) && isPacked(handle)) {
- // It's a packed chunk in the data files, packed,
- // so we have to read it in completely and unpack it
-
- closeData(handle);
+ if (!file.packed)
+ return rawData;
- uint32 size = getDataSize(path);
- byte *data = getData(path);
+ byte *unpackedData = unpack(rawData, file.size, size);
- return new DataStream(data, size);
+ delete[] rawData;
- } else
- // Otherwise, we can just return a stream
- return openAsStream(handle, true);
+ return unpackedData;
}
} // End of namespace Gob
diff --git a/engines/gob/dataio.h b/engines/gob/dataio.h
index 6a86667e1b..6e12d15af8 100644
--- a/engines/gob/dataio.h
+++ b/engines/gob/dataio.h
@@ -27,113 +27,82 @@
#define GOB_DATAIO_H
#include "common/endian.h"
+#include "common/str.h"
+#include "common/hashmap.h"
+#include "common/array.h"
#include "common/file.h"
-namespace Gob {
-
-#define MAX_FILES 30
-#define MAX_DATA_FILES 8
-#define MAX_SLOT_COUNT 8
-
-class DataIO;
-
-class DataStream : public Common::SeekableReadStream {
-public:
- DataStream(DataIO &io, int16 handle, uint32 dSize, bool dispose = false);
- DataStream(byte *buf, uint32 dSize, bool dispose = true);
- virtual ~DataStream();
-
- virtual int32 pos() const;
- virtual int32 size() const;
+namespace Common {
+ class SeekableReadStream;
+}
- virtual bool seek(int32 offset, int whence = SEEK_SET);
-
- virtual bool eos() const;
-
- virtual uint32 read(void *dataPtr, uint32 dataSize);
+namespace Gob {
-private:
- DataIO *_io;
- int16 _handle;
- uint32 _size;
- byte *_data;
- bool _dispose;
- Common::MemoryReadStream *_stream;
+struct ArchiveInfo {
+ Common::String name;
+ bool base;
+ uint32 fileCount;
};
class DataIO {
public:
- struct ChunkDesc {
- char chunkName[13];
- uint32 size;
- uint32 offset;
- byte packed;
- ChunkDesc() : size(0), offset(0), packed(0) { chunkName[0] = 0; }
- };
-
- int32 unpackData(byte *src, byte *dest);
+ DataIO();
+ ~DataIO();
- void openDataFile(const char *src, bool itk = 0);
- void closeDataFile(bool itk = 0);
+ void getArchiveInfo(Common::Array<ArchiveInfo> &info) const;
- byte *getUnpackedData(const char *name);
+ bool openArchive(Common::String name, bool base);
+ bool closeArchive(bool base);
- void closeData(int16 handle);
- int16 openData(const char *path);
- bool existData(const char *path);
+ bool hasFile(const Common::String &name);
- DataStream *openAsStream(int16 handle, bool dispose = false);
+ int32 fileSize(const Common::String &name);
- int32 getDataSize(const char *name);
- byte *getData(const char *path);
- DataStream *getDataStream(const char *path);
+ Common::SeekableReadStream *getFile(const Common::String &name);
+ byte *getFile(const Common::String &name, int32 &size);
- DataIO(class GobEngine *vm);
- ~DataIO();
+ static byte *unpack(const byte *src, uint32 srcSize, int32 &size);
+ static Common::SeekableReadStream *unpack(Common::SeekableReadStream &src);
-protected:
- Common::File _filesHandles[MAX_FILES];
+private:
+ static const int kMaxArchives = 8;
- ChunkDesc *_dataFiles [MAX_DATA_FILES];
- uint16 _numDataChunks [MAX_DATA_FILES];
- int16 _dataFileHandles[MAX_DATA_FILES];
- bool _dataFileItk [MAX_DATA_FILES];
+ struct Archive;
- ChunkDesc *_chunk [MAX_SLOT_COUNT * MAX_DATA_FILES];
- int32 _chunkPos [MAX_SLOT_COUNT * MAX_DATA_FILES];
- bool _isCurrentSlot[MAX_SLOT_COUNT * MAX_DATA_FILES];
+ struct File {
+ Common::String name;
+ uint32 size;
+ uint32 offset;
+ bool packed;
- class GobEngine *_vm;
+ Archive *archive;
- bool isDataFileChunk(int16 handle) const;
- bool isPacked (int16 handle) const;
+ File();
+ File(const Common::String &n, uint32 s, uint32 o, bool p, Archive &a);
+ };
- int getFile (int16 handle) const;
- int getSlot (int16 handle) const;
- int getIndex(int16 handle) const;
+ typedef Common::HashMap<Common::String, File, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
- int getIndex (int file, int slot) const;
- int16 getHandle(int file, int slot) const;
+ struct Archive {
+ Common::String name;
+ Common::File file;
- int16 file_open(const char *path);
- Common::File *file_getHandle(int16 handle);
- const Common::File *file_getHandle(int16 handle) const;
+ FileMap files;
- int16 getChunk(const char *chunkName);
- char freeChunk(int16 handle);
- int32 readChunk(int16 handle, byte *buf, uint16 size);
- int16 seekChunk(int16 handle, int32 pos, int16 from);
+ bool base;
+ };
- uint32 getChunkPos(int16 handle) const;
+ Common::Array<Archive *> _archives;
- int32 getChunkSize(const char *chunkName, int32 &packSize);
+ Archive *openArchive(const Common::String &name);
+ bool closeArchive(Archive &archive);
- uint32 getPos(int16 handle);
- void seekData(int16 handle, int32 pos, int16 from);
+ File *findFile(const Common::String &name);
- int32 readData(int16 handle, byte *buf, uint16 size);
+ Common::SeekableReadStream *getFile(File &file);
+ byte *getFile(File &file, int32 &size);
- friend class DataStream;
+ static void unpack(Common::SeekableReadStream &src, byte *dest, uint32 size);
};
} // End of namespace Gob
diff --git a/engines/gob/demos/demoplayer.cpp b/engines/gob/demos/demoplayer.cpp
index 4ceca3ce24..9013f600a5 100644
--- a/engines/gob/demos/demoplayer.cpp
+++ b/engines/gob/demos/demoplayer.cpp
@@ -25,10 +25,10 @@
#include "common/endian.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "gob/gob.h"
#include "gob/demos/demoplayer.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/draw.h"
diff --git a/engines/gob/detection_tables.h b/engines/gob/detection_tables.h
index 93c1cc6d1c..43f15b8845 100644
--- a/engines/gob/detection_tables.h
+++ b/engines/gob/detection_tables.h
@@ -2264,16 +2264,14 @@ static const GOBGameDescription gameDescriptions[] = {
kFeaturesAdLib,
"demo.stk", "demo.tot", 0
},
-// This version is not detected on purpose: it's a pirated version, using a corrupted crack.
-// Tagged ADGF_PIRATED! Do not re-add nor un-tag!
- {
+ { // Supplied by scoriae
{
"fascination",
- "",
+ "VGA",
AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
- UNK_LANG,
+ EN_ANY,
kPlatformPC,
- ADGF_PIRATED,
+ ADGF_NO_FLAGS,
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeFascination,
diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp
index 960f4e9e34..ae1bbd4e8e 100644
--- a/engines/gob/draw.cpp
+++ b/engines/gob/draw.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/draw.h"
@@ -328,7 +329,7 @@ void Draw::adjustCoords(char adjust, int16 *coord1, int16 *coord2) {
if (coord2)
*coord2 *= 2;
if (coord1)
- *coord2 *= 2;
+ *coord1 *= 2;
break;
case 1:
@@ -470,10 +471,8 @@ void Draw::oPlaytoons_sub_F_1B(uint16 id, int16 left, int16 top, int16 right, in
else
WRITE_VAR(24, (uint32) 0);
WRITE_VAR(25, (uint32) shortId);
- if (_hotspotText) {
- strncpy(_hotspotText, paramStr, 40);
- _hotspotText[39] = 0;
- }
+ if (_hotspotText)
+ Common::strlcpy(_hotspotText, paramStr, 40);
}
_vm->_inter->funcBlock(0);
_vm->_game->_script->pop();
@@ -640,10 +639,11 @@ void Draw::wobble(Surface &surfDesc) {
}
Font *Draw::loadFont(const char *path) const {
- if (!_vm->_dataIO->existData(path))
+ if (!_vm->_dataIO->hasFile(path))
return 0;
- byte *data = _vm->_dataIO->getData(path);
+ int32 size;
+ byte *data = _vm->_dataIO->getFile(path, size);
return new Font(data);
}
diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp
index 8d8f040924..fc5521a959 100644
--- a/engines/gob/draw_playtoons.cpp
+++ b/engines/gob/draw_playtoons.cpp
@@ -151,7 +151,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
case DRAW_PUTPIXEL:
switch (_pattern & 0xFF) {
- case -1:
+ case 0xFF:
warning("oPlaytoons_spriteOperation: operation DRAW_PUTPIXEL, pattern -1");
break;
case 1:
diff --git a/engines/gob/draw_v1.cpp b/engines/gob/draw_v1.cpp
index 6496229282..d130f424c2 100644
--- a/engines/gob/draw_v1.cpp
+++ b/engines/gob/draw_v1.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "graphics/cursorman.h"
#include "gob/gob.h"
#include "gob/draw.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/game.h"
@@ -258,7 +258,7 @@ void Draw_v1::printTotText(int16 id) {
} else if (cmd == 1) {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
- strncpy0(buf, GET_VARO_STR(val), 19);
+ Common::strlcpy(buf, GET_VARO_STR(val), 20);
} else {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp
index bb2a57b790..ec678644d5 100644
--- a/engines/gob/draw_v2.cpp
+++ b/engines/gob/draw_v2.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "graphics/cursorman.h"
#include "gob/gob.h"
#include "gob/draw.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/game.h"
@@ -550,7 +550,7 @@ void Draw_v2::printTotText(int16 id) {
sprintf(buf, "%d", VAR_OFFSET(val));
} else if (cmd == 1) {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
- strncpy0(buf, GET_VARO_STR(val), 19);
+ Common::strlcpy(buf, GET_VARO_STR(val), 20);
} else {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
sprintf(buf, "%d", VAR_OFFSET(val));
diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp
index f77b3e946a..98c1066cb0 100644
--- a/engines/gob/game.cpp
+++ b/engines/gob/game.cpp
@@ -24,10 +24,10 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/game.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/variables.h"
@@ -247,7 +247,7 @@ void Game::playTot(int16 skipPlay) {
int16 *oldCaptureCounter;
int16 *oldBreakFrom;
int16 *oldNestLevel;
- int16 _captureCounter;
+ int16 captureCounter = 0;
int16 breakFrom;
int16 nestLevel;
@@ -259,7 +259,7 @@ void Game::playTot(int16 skipPlay) {
_vm->_inter->_nestLevel = &nestLevel;
_vm->_inter->_breakFromLevel = &breakFrom;
- _vm->_scenery->_pCaptureCounter = &_captureCounter;
+ _vm->_scenery->_pCaptureCounter = &captureCounter;
strcpy(savedTotName, _curTotFile);
if (skipPlay <= 0) {
@@ -319,10 +319,12 @@ void Game::playTot(int16 skipPlay) {
_vm->_inter->renewTimeInVars();
- WRITE_VAR(13, _vm->_global->_useMouse);
- WRITE_VAR(14, _vm->_global->_soundFlags);
- WRITE_VAR(15, _vm->_global->_fakeVideoMode);
- WRITE_VAR(16, _vm->_global->_language);
+ if (_vm->_inter->_variables) {
+ WRITE_VAR(13, _vm->_global->_useMouse);
+ WRITE_VAR(14, _vm->_global->_soundFlags);
+ WRITE_VAR(15, _vm->_global->_fakeVideoMode);
+ WRITE_VAR(16, _vm->_global->_language);
+ }
_vm->_inter->callSub(2);
@@ -357,7 +359,8 @@ void Game::playTot(int16 skipPlay) {
if (_totToLoad[0] == 0)
break;
- strcpy(_curTotFile, _totToLoad);
+ Common::strlcpy(_curTotFile, _totToLoad, 14);
+
}
} else {
_vm->_inter->initControlVars(0);
@@ -370,7 +373,7 @@ void Game::playTot(int16 skipPlay) {
_vm->_inter->_terminate = 2;
}
- strcpy(_curTotFile, savedTotName);
+ Common::strlcpy(_curTotFile, savedTotName, 14);
_vm->_inter->_nestLevel = oldNestLevel;
_vm->_inter->_breakFromLevel = oldBreakFrom;
@@ -575,7 +578,7 @@ void Game::totSub(int8 flags, const char *newTotFile) {
if (flags & 1)
_vm->_inter->_variables = 0;
- strncpy0(_curTotFile, newTotFile, 9);
+ Common::strlcpy(_curTotFile, newTotFile, 10);
strcat(_curTotFile, ".TOT");
if (_vm->_inter->_terminate != 0) {
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index 65c960bd73..8c8d77a9a6 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -28,12 +28,13 @@
#include "common/events.h"
#include "common/EventRecorder.h"
+#include "backends/audiocd/audiocd.h"
#include "base/plugins.h"
#include "common/config-manager.h"
#include "common/md5.h"
#include "sound/mididrv.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
#include "gui/dialog.h"
#include "gui/widget.h"
@@ -136,6 +137,8 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst) {
_copyProtection = ConfMan.getBool("copy_protection");
+ _console = new GobConsole(this);
+
DebugMan.addDebugChannel(kDebugFuncOp, "FuncOpcodes", "Script FuncOpcodes debug level");
DebugMan.addDebugChannel(kDebugDrawOp, "DrawOpcodes", "Script DrawOpcodes debug level");
DebugMan.addDebugChannel(kDebugGobOp, "GoblinOpcodes", "Script GoblinOpcodes debug level");
@@ -153,6 +156,8 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst) {
}
GobEngine::~GobEngine() {
+ delete _console;
+
deinitGameParts();
}
@@ -366,7 +371,7 @@ bool GobEngine::initGameParts() {
_global = new Global(this);
_util = new Util(this);
- _dataIO = new DataIO(this);
+ _dataIO = new DataIO();
_palAnim = new PalAnim(this);
_vidPlayer = new VideoPlayer(this);
_sound = new Sound(this);
diff --git a/engines/gob/gob.h b/engines/gob/gob.h
index f6c03fa617..fe69e27c01 100644
--- a/engines/gob/gob.h
+++ b/engines/gob/gob.h
@@ -32,6 +32,8 @@
#include "engines/engine.h"
+#include "gob/console.h"
+
namespace GUI {
class StaticTextWidget;
}
@@ -41,8 +43,14 @@ namespace GUI {
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Gobliiins
+ * - Gobliins 2
+ * - Goblins 3
+ * - Ween: The Prophecy
+ * - Bargon Attack
+ * - Lost in Time
+ * - The Bizarre Adventures of Woodruff and the Schnibble
*/
namespace Gob {
@@ -62,6 +70,7 @@ class PalAnim;
class Scenery;
class Util;
class SaveLoad;
+class GobConsole;
#define WRITE_VAR_UINT32(var, val) _vm->_inter->_variables->writeVar32(var, val)
#define WRITE_VAR_UINT16(var, val) _vm->_inter->_variables->writeVar16(var, val)
@@ -152,6 +161,7 @@ private:
GameType _gameType;
int32 _features;
Common::Platform _platform;
+ GobConsole *_console;
uint32 _pauseStart;
@@ -221,6 +231,8 @@ public:
bool isTrueColor() const;
bool isDemo() const;
+ GUI::Debugger *getDebugger() { return _console; }
+
const Graphics::PixelFormat &getPixelFormat() const;
GobEngine(OSystem *syst);
diff --git a/engines/gob/goblin.cpp b/engines/gob/goblin.cpp
index ee2b4f52c9..402b33d5fd 100644
--- a/engines/gob/goblin.cpp
+++ b/engines/gob/goblin.cpp
@@ -23,9 +23,10 @@
*
*/
+#include "common/str.h"
+
#include "gob/gob.h"
#include "gob/goblin.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/draw.h"
@@ -668,11 +669,11 @@ void Goblin::adjustDest(int16 posX, int16 posY) {
resDelta = i;
}
- for (i = 1; ((i + _pressedMapX) < _vm->_map->_mapWidth) &&
+ for (i = 1; ((i + _pressedMapX) < _vm->_map->getMapWidth()) &&
(_vm->_map->getPass(_pressedMapX + i, _pressedMapY) == 0); i++)
;
- if ((_pressedMapX + i) < _vm->_map->_mapWidth) {
+ if ((_pressedMapX + i) < _vm->_map->getMapWidth()) {
deltaPix = (i * 12) - (posX % 12);
if ((resDelta == -1) || (deltaPix < resDeltaPix)) {
resDeltaPix = deltaPix;
@@ -681,11 +682,11 @@ void Goblin::adjustDest(int16 posX, int16 posY) {
}
}
- for (i = 1; ((i + _pressedMapY) < _vm->_map->_mapHeight) &&
+ for (i = 1; ((i + _pressedMapY) < _vm->_map->getMapHeight()) &&
(_vm->_map->getPass(_pressedMapX, _pressedMapY + i) == 0); i++)
;
- if ((_pressedMapY + i) < _vm->_map->_mapHeight) {
+ if ((_pressedMapY + i) < _vm->_map->getMapHeight()) {
deltaPix = (i * 6) - (posY % 6);
if ((resDelta == -1) || (deltaPix < resDeltaPix)) {
resDeltaPix = deltaPix;
@@ -726,8 +727,8 @@ void Goblin::adjustDest(int16 posX, int16 posY) {
}
}
- _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->getMapHeight() - 1);
}
void Goblin::adjustTarget() {
@@ -737,18 +738,18 @@ void Goblin::adjustTarget() {
if ((_pressedMapY > 0) &&
(_vm->_map->getItem(_pressedMapX, _pressedMapY - 1) != 0)) {
_pressedMapY--;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY) != 0)) {
_pressedMapX++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_pressedMapY > 0) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY - 1) != 0)) {
_pressedMapY--;
_pressedMapX++;
}
}
- _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->getMapHeight() - 1);
}
void Goblin::targetDummyItem(Gob_Object *gobDesc) {
@@ -847,7 +848,7 @@ void Goblin::targetItem() {
}
}
- if (_pressedMapY < (_vm->_map->_mapHeight-1)) {
+ if (_pressedMapY < (_vm->_map->getMapHeight()-1)) {
if ((_vm->_map->getItem(_pressedMapX, _pressedMapY + 1)) ==
(_vm->_map->getItem(_pressedMapX, _pressedMapY))) {
_pressedMapY++;
@@ -898,8 +899,8 @@ void Goblin::targetItem() {
}
}
}
- _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->getMapHeight() - 1);
}
void Goblin::moveFindItem(int16 posX, int16 posY) {
@@ -933,23 +934,23 @@ void Goblin::moveFindItem(int16 posX, int16 posY) {
break;
}
- _pressedMapX = CLIP(posX / 12, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP(posY / 6, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP(posX / 12, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP(posY / 6, 0, _vm->_map->getMapHeight() - 1);
if ((_vm->_map->getItem(_pressedMapX, _pressedMapY) == 0) && (i < 20)) {
- if ((_pressedMapY < (_vm->_map->_mapHeight - 1)) &&
+ if ((_pressedMapY < (_vm->_map->getMapHeight() - 1)) &&
(_vm->_map->getItem(_pressedMapX, _pressedMapY + 1) != 0)) {
_pressedMapY++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
- (_pressedMapY < (_vm->_map->_mapHeight - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
+ (_pressedMapY < (_vm->_map->getMapHeight() - 1)) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY + 1) != 0)) {
_pressedMapX++;
_pressedMapY++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY) != 0)) {
_pressedMapX++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_pressedMapY > 0) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY - 1) != 0)) {
_pressedMapX++;
@@ -965,15 +966,15 @@ void Goblin::moveFindItem(int16 posX, int16 posY) {
(_vm->_map->getItem(_pressedMapX - 1, _pressedMapY) != 0)) {
_pressedMapX--;
} else if ((_pressedMapX > 0) &&
- (_pressedMapY < (_vm->_map->_mapHeight - 1)) &&
+ (_pressedMapY < (_vm->_map->getMapHeight() - 1)) &&
(_vm->_map->getItem(_pressedMapX - 1, _pressedMapY + 1) != 0)) {
_pressedMapX--;
_pressedMapY++;
}
}
} else {
- _pressedMapX = CLIP(posX / 12, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP(posY / 6, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP(posX / 12, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP(posY / 6, 0, _vm->_map->getMapHeight() - 1);
}
}
@@ -1186,7 +1187,7 @@ void Goblin::loadObjects(const char *source) {
freeObjects();
initList();
- strncpy0(_vm->_map->_sourceFile, source, 14);
+ Common::strlcpy(_vm->_map->_sourceFile, source, 15);
_vm->_map->_sourceFile[strlen(_vm->_map->_sourceFile) - 4] = 0;
_vm->_map->loadMapObjects(source);
@@ -1386,8 +1387,8 @@ void Goblin::pickItem(int16 indexToPocket, int16 idToPocket) {
_itemIndInPocket = indexToPocket;
_itemIdInPocket = idToPocket;
- for (int y = 0; y < _vm->_map->_mapHeight; y++) {
- for (int x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (int y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (int x = 0; x < _vm->_map->getMapWidth(); x++) {
if (_itemByteFlag == 1) {
if (((_vm->_map->getItem(x, y) & 0xFF00) >> 8) == idToPocket)
_vm->_map->setItem(x, y, _vm->_map->getItem(x, y) & 0xFF);
@@ -1450,7 +1451,7 @@ void Goblin::placeItem(int16 indexInPocket, int16 idInPocket) {
_vm->_map->placeItem(xPos, yPos - 1, idInPocket);
if (lookDir == 4) {
- if (xPos < _vm->_map->_mapWidth - 1) {
+ if (xPos < _vm->_map->getMapWidth() - 1) {
_vm->_map->placeItem(xPos + 1, yPos, idInPocket);
if (yPos > 0)
@@ -1497,16 +1498,16 @@ void Goblin::swapItems(int16 indexToPick, int16 idToPick) {
_itemIdInPocket = idToPick;
if (_itemByteFlag == 0) {
- for (y = 0; y < _vm->_map->_mapHeight; y++) {
- for (x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (x = 0; x < _vm->_map->getMapWidth(); x++) {
if ((_vm->_map->getItem(x, y) & 0xFF) == idToPick)
_vm->_map->setItem(x, y, (_vm->_map->getItem(x, y) & 0xFF00) + idToPlace);
}
}
} else {
- for (y = 0; y < _vm->_map->_mapHeight; y++) {
- for (x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (x = 0; x < _vm->_map->getMapWidth(); x++) {
if (((_vm->_map->getItem(x, y) & 0xFF00) >> 8) == idToPick)
_vm->_map->setItem(x, y, (_vm->_map->getItem(x, y) & 0xFF) + (idToPlace << 8));
}
@@ -1704,15 +1705,15 @@ void Goblin::setState(int16 index, int16 state) {
animData->newCycle = _vm->_scenery->getAnimLayer(animation, layer)->framesCount;
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 1);
- if (_vm->_map->_bigTiles) {
- *obj->pPosY = ((obj->goblinY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles()) {
+ *obj->pPosY = ((obj->goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
(obj->goblinY + 1) / 2;
} else {
- *obj->pPosY = (obj->goblinY + 1) * _vm->_map->_tilesHeight -
+ *obj->pPosY = (obj->goblinY + 1) * _vm->_map->getTilesHeight() -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
}
- *obj->pPosX = obj->goblinX * _vm->_map->_tilesWidth;
+ *obj->pPosX = obj->goblinX * _vm->_map->getTilesWidth();
}
void Goblin::animate(Mult::Mult_Object *obj) {
@@ -1780,40 +1781,69 @@ void Goblin::animate(Mult::Mult_Object *obj) {
}
void Goblin::move(int16 destX, int16 destY, int16 objIndex) {
- Mult::Mult_Object *obj;
- Mult::Mult_AnimData *animData;
- int16 mouseX;
- int16 mouseY;
- int16 gobDestX;
- int16 gobDestY;
- obj = &_vm->_mult->_objects[objIndex];
- animData = obj->pAnimData;
+ Mult::Mult_Object *obj = &_vm->_mult->_objects[objIndex];
+ Mult::Mult_AnimData *animData = obj->pAnimData;
- obj->gobDestX = destX;
- obj->gobDestY = destY;
+ obj->gobDestX = destX;
+ obj->gobDestY = destY;
animData->destX = destX;
animData->destY = destY;
if (animData->isBusy != 0) {
if ((destX == -1) && (destY == -1)) {
- mouseX = _vm->_global->_inter_mouseX;
- mouseY = _vm->_global->_inter_mouseY;
- if (_vm->_map->_bigTiles)
- mouseY += ((_vm->_global->_inter_mouseY / _vm->_map->_tilesHeight) + 1) / 2;
+ int16 mouseX = _vm->_global->_inter_mouseX;
+ int16 mouseY = _vm->_global->_inter_mouseY;
+
+ if (_vm->_map->hasBigTiles())
+ mouseY += ((_vm->_global->_inter_mouseY / _vm->_map->getTilesHeight()) + 1) / 2;
- gobDestX = mouseX / _vm->_map->_tilesWidth;
- gobDestY = mouseY / _vm->_map->_tilesHeight;
+ int16 gobDestX = mouseX / _vm->_map->getTilesWidth();
+ int16 gobDestY = mouseY / _vm->_map->getTilesHeight();
if (_vm->_map->getPass(gobDestX, gobDestY) == 0)
_vm->_map->findNearestWalkable(gobDestX, gobDestY, mouseX, mouseY);
- animData->destX = obj->gobDestX =
- (gobDestX == -1) ? obj->goblinX : gobDestX;
- animData->destY = obj->gobDestY =
- (gobDestY == -1) ? obj->goblinY : gobDestY;
+ obj->gobDestX = (gobDestX == -1) ? obj->goblinX : gobDestX;
+ obj->gobDestY = (gobDestY == -1) ? obj->goblinY : gobDestY;
+
+ animData->destX = obj->gobDestX;
+ animData->destY = obj->gobDestY;
+ }
+ }
+
+ WRITE_VAR(56, 0);
+
+ byte passType = _vm->_map->getPass(obj->gobDestX, obj->gobDestY);
+
+ // Prevent continuous walking on wide stairs
+ if (passType == 11) {
+ if (_vm->_map->getScreenWidth() == 640) {
+ obj->gobDestY++;
+ animData->destY++;
}
}
+
+ // Prevent stopping in the middle of big ladders
+ if ((passType == 19) || (passType == 20)) {
+ int ladderTop = 0;
+ while (_vm->_map->getPass(obj->gobDestX, obj->gobDestY + ladderTop) == passType)
+ ladderTop++;
+
+ int ladderBottom = 0;
+ while (_vm->_map->getPass(obj->gobDestX, obj->gobDestY + ladderBottom) == passType)
+ ladderBottom--;
+
+ int ladderDest;
+ if (ABS(ladderBottom) <= ladderTop)
+ ladderDest = obj->gobDestY + ladderBottom;
+ else
+ ladderDest = obj->gobDestY + ladderTop;
+
+ obj->gobDestY = ladderDest;
+ animData->destY = ladderDest;
+ }
+
initiateMove(obj);
}
diff --git a/engines/gob/goblin_v1.cpp b/engines/gob/goblin_v1.cpp
index f55fec433c..3dc4c6611d 100644
--- a/engines/gob/goblin_v1.cpp
+++ b/engines/gob/goblin_v1.cpp
@@ -154,8 +154,10 @@ void Goblin_v1::initiateMove(Mult::Mult_Object *obj) {
_vm->_map->_nearestWayPoint, _vm->_map->_nearestDest) == 0) {
_pathExistence = 0;
} else {
- _vm->_map->_destX = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
}
}
}
@@ -173,10 +175,10 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
_pathExistence = 0;
}
- nextAct = _vm->_map->getDirection(_vm->_map->_curGoblinX,
+ nextAct = (int16) _vm->_map->getDirection(_vm->_map->_curGoblinX,
_vm->_map->_curGoblinY, _vm->_map->_destX, _vm->_map->_destY);
- if (nextAct == 0)
+ if (nextAct == kDirNone)
_pathExistence = 0;
} else if (_pathExistence == 3) {
_vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
@@ -199,20 +201,20 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
if (_vm->_map->_nearestWayPoint > _vm->_map->_nearestDest) {
_vm->_map->optimizePoints(0, 0, 0);
- _vm->_map->_destX =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
if (_vm->_map->_nearestWayPoint > _vm->_map->_nearestDest)
_vm->_map->_nearestWayPoint--;
} else if (_vm->_map->_nearestWayPoint < _vm->_map->_nearestDest) {
_vm->_map->optimizePoints(0, 0, 0);
- _vm->_map->_destX =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
if (_vm->_map->_nearestWayPoint < _vm->_map->_nearestDest)
_vm->_map->_nearestWayPoint++;
@@ -220,8 +222,12 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
if ((_vm->_map->checkDirectPath(0, _vm->_map->_curGoblinX,
_vm->_map->_curGoblinY, _gobDestX, _gobDestY) == 3) &&
(_vm->_map->getPass(_pressedMapX, _pressedMapY) != 0)) {
- _vm->_map->_destX = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
+
} else {
_pathExistence = 1;
_vm->_map->_destX = _pressedMapX;
@@ -229,7 +235,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
}
}
}
- nextAct = _vm->_map->getDirection(_vm->_map->_curGoblinX,
+ nextAct = (int16) _vm->_map->getDirection(_vm->_map->_curGoblinX,
_vm->_map->_curGoblinY, _vm->_map->_destX, _vm->_map->_destY);
}
}
@@ -238,11 +244,11 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
nextAct = 0x4DC8;
switch (nextAct) {
- case Map::kDirW:
+ case kDirW:
gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
break;
- case Map::kDirE:
+ case kDirE:
gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
break;
@@ -254,7 +260,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = 23;
break;
- case Map::kDirN:
+ case kDirN:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY - 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -275,7 +281,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 2);
break;
- case Map::kDirS:
+ case kDirS:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY + 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -296,7 +302,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 6);
break;
- case Map::kDirSE:
+ case kDirSE:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX + 1, _vm->_map->_curGoblinY + 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -310,7 +316,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
break;
- case Map::kDirSW:
+ case kDirSW:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX - 1, _vm->_map->_curGoblinY + 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -324,7 +330,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
break;
- case Map::kDirNW:
+ case kDirNW:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX - 1, _vm->_map->_curGoblinY - 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -338,7 +344,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
break;
- case Map::kDirNE:
+ case kDirNE:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX + 1, _vm->_map->_curGoblinY - 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
diff --git a/engines/gob/goblin_v2.cpp b/engines/gob/goblin_v2.cpp
index 2747750abb..503377c19b 100644
--- a/engines/gob/goblin_v2.cpp
+++ b/engines/gob/goblin_v2.cpp
@@ -39,10 +39,10 @@ namespace Gob {
Goblin_v2::Goblin_v2(GobEngine *vm) : Goblin_v1(vm) {
_gobsCount = -1;
- _rotStates[0][0] = 0; _rotStates[0][1] = 18; _rotStates[0][2] = 19; _rotStates[0][3] = 20;
- _rotStates[1][0] = 13; _rotStates[1][1] = 2; _rotStates[1][2] = 12; _rotStates[1][3] = 14;
- _rotStates[2][0] = 16; _rotStates[2][1] = 15; _rotStates[2][2] = 4; _rotStates[2][3] = 17;
- _rotStates[3][0] = 23; _rotStates[3][1] = 21; _rotStates[3][2] = 22; _rotStates[3][3] = 6;
+ _rotStates[0][0] = 0; _rotStates[0][1] = 18; _rotStates[0][2] = 19; _rotStates[0][3] = 20;
+ _rotStates[1][0] = 13; _rotStates[1][1] = 2; _rotStates[1][2] = 12; _rotStates[1][3] = 14;
+ _rotStates[2][0] = 16; _rotStates[2][1] = 15; _rotStates[2][2] = 4; _rotStates[2][3] = 17;
+ _rotStates[3][0] = 23; _rotStates[3][1] = 21; _rotStates[3][2] = 22; _rotStates[3][3] = 6;
}
void Goblin_v2::freeObjects() {
@@ -80,13 +80,13 @@ void Goblin_v2::placeObject(Gob_Object *objDesc, char animated,
objAnim->newCycle = 0;
_vm->_scenery->updateAnim(objAnim->layer, 0, objAnim->animation, 0,
*obj->pPosX, *obj->pPosY, 0);
- if (!_vm->_map->_bigTiles)
- *obj->pPosY = (y + 1) * _vm->_map->_tilesHeight
+ if (!_vm->_map->hasBigTiles())
+ *obj->pPosY = (y + 1) * _vm->_map->getTilesHeight()
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
else
- *obj->pPosY = ((y + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((y + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (y + 1) / 2;
- *obj->pPosX = x * _vm->_map->_tilesWidth;
+ *obj->pPosX = x * _vm->_map->getTilesWidth();
} else {
if ((obj->goblinStates != 0) && (obj->goblinStates[state] != 0)) {
layer = obj->goblinStates[state][0].layer;
@@ -99,13 +99,13 @@ void Goblin_v2::placeObject(Gob_Object *objDesc, char animated,
objAnim->isStatic = 0;
objAnim->newCycle = _vm->_scenery->getAnimLayer(animation, layer)->framesCount;
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (!_vm->_map->_bigTiles)
- *obj->pPosY = (y + 1) * _vm->_map->_tilesHeight
+ if (!_vm->_map->hasBigTiles())
+ *obj->pPosY = (y + 1) * _vm->_map->getTilesHeight()
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
else
- *obj->pPosY = ((y + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((y + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (y + 1) / 2;
- *obj->pPosX = x * _vm->_map->_tilesWidth;
+ *obj->pPosX = x * _vm->_map->getTilesWidth();
initiateMove(obj);
} else
initiateMove(obj);
@@ -121,54 +121,55 @@ void Goblin_v2::initiateMove(Mult::Mult_Object *obj) {
obj->pAnimData->pathExistence = _vm->_map->checkDirectPath(obj,
obj->goblinX, obj->goblinY, obj->gobDestX, obj->gobDestY);
if (obj->pAnimData->pathExistence == 3) {
- obj->destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- obj->destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ obj->destX = wayPoint.x;
+ obj->destY = wayPoint.y;
}
}
void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16 nextAct) {
- Mult::Mult_AnimData *animData;
- int16 framesCount;
- int16 gobX;
- int16 gobY;
- int16 gobDestX;
- int16 gobDestY;
- int16 destX;
- int16 destY;
- int16 dir;
-
- dir = 0;
- animData = obj->pAnimData;
- framesCount = _vm->_scenery->getAnimLayer(animData->animation, animData->layer)->framesCount;
- animData->newCycle = framesCount;
- gobX = obj->goblinX;
- gobY = obj->goblinY;
- animData->order = gobY;
- gobDestX = obj->gobDestX;
- gobDestY = obj->gobDestY;
+ Mult::Mult_AnimData *animData = obj->pAnimData;
+
+ animData->newCycle = _vm->_scenery->getAnimLayer(animData->animation, animData->layer)->framesCount;
+
+ int16 gobX = obj->goblinX;
+ int16 gobY = obj->goblinY;
+ int16 destX = obj->destX;
+ int16 destY = obj->destY;
+ int16 gobDestX = obj->gobDestX;
+ int16 gobDestY = obj->gobDestY;
+
animData->destX = gobDestX;
animData->destY = gobDestY;
- destX = obj->destX;
- destY = obj->destY;
+ animData->order = gobY;
+
+ Direction dir = kDirNone;
if (animData->pathExistence == 1) {
+
dir = _vm->_map->getDirection(gobX, gobY, destX, destY);
- if (dir == 0)
+ if (dir == kDirNone)
animData->pathExistence = 0;
- if ((gobX == destX) && (gobY == destY))
+ if ((gobX == gobDestX) && (gobY == gobDestY))
animData->pathExistence = 4;
+
} else if (animData->pathExistence == 3) {
- if ((gobX == gobDestX) && (gobY == gobDestY)) {
- animData->pathExistence = 4;
- destX = gobDestX;
- destY = gobDestY;
- } else {
+
+ if ((gobX != gobDestX) || (gobY != gobDestY)) {
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) != 1) {
+
if ((gobX == destX) && (gobY == destY)) {
+
if (obj->nearestWayPoint > obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -177,8 +178,12 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
obj->nearestWayPoint--;
} else if (obj->nearestWayPoint < obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -188,8 +193,12 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
} else {
if ((_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) == 3) &&
(_vm->_map->getPass(gobDestX, gobDestY) != 0)) {
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
WRITE_VAR(56, 1);
} else {
animData->pathExistence = 1;
@@ -197,26 +206,35 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
destY = gobDestY;
}
}
+
}
+
} else {
destX = gobDestX;
destY = gobDestY;
}
+
dir = _vm->_map->getDirection(gobX, gobY, destX, destY);
+
+ } else {
+ animData->pathExistence = 4;
+ destX = gobDestX;
+ destY = gobDestY;
}
+
}
- obj->goblinX = gobX;
- obj->goblinY = gobY;
- obj->gobDestX = gobDestX;
- obj->gobDestY = gobDestY;
- obj->destX = destX;
- obj->destY = destY;
+ obj->goblinX = gobX;
+ obj->goblinY = gobY;
+ obj->destX = destX;
+ obj->destY = destY;
+ obj->gobDestX = gobDestX;
+ obj->gobDestY = gobDestY;
switch (dir) {
- case Map::kDirNW:
+ case kDirNW:
animData->nextState = 1;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 40;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
@@ -224,28 +242,29 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirN:
+ case kDirN:
animData->nextState =
(animData->curLookDir == 2) ? 2 : rotateState(animData->curLookDir, 2);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
- if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
- if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
- animData->nextState = 42;
- else
- animData->nextState = 2;
- } else
+ if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) == 10)
animData->nextState = 40;
- } else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
+ else if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
+ animData->nextState = 42;
+ else
+ animData->nextState = 2;
+ }
+
+ if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
animData->nextState = 38;
- else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
+ if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
animData->nextState = 26;
}
break;
- case Map::kDirNE:
- animData->nextState = 3;
- if (_vm->_map->_screenWidth == 640) {
+ case kDirNE:
+ animData->nextState = 3;
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 42;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
@@ -253,17 +272,17 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirW:
+ case kDirW:
animData->nextState = rotateState(animData->curLookDir, 0);
break;
- case Map::kDirE:
+ case kDirE:
animData->nextState = rotateState(animData->curLookDir, 4);
break;
- case Map::kDirSW:
+ case kDirSW:
animData->nextState = 7;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 41;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY + 2) != 10)
@@ -271,10 +290,10 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirS:
+ case kDirS:
animData->nextState =
(animData->curLookDir == 6) ? 6 : rotateState(animData->curLookDir, 6);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
animData->nextState = 39;
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
@@ -282,9 +301,9 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirSE:
+ case kDirSE:
animData->nextState = 5;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 43;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY + 2) != 10)
@@ -293,7 +312,7 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
break;
default:
- if (animData->curLookDir == 0)
+ if (animData->curLookDir == 0)
animData->nextState = 8;
else if (animData->curLookDir == 2)
animData->nextState = 29;
@@ -307,12 +326,6 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
int16 nextAct, int16 framesCount) {
- Mult::Mult_AnimData *animData;
- int16 gobX;
- int16 gobY;
- int16 animation;
- int16 state;
- int16 layer;
if (!obj->goblinStates)
return;
@@ -320,7 +333,7 @@ void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
movePathFind(obj, 0, 0);
playSounds(obj);
- animData = obj->pAnimData;
+ Mult::Mult_AnimData *animData = obj->pAnimData;
framesCount = _vm->_scenery->getAnimLayer(animData->animation, animData->layer)->framesCount;
@@ -395,72 +408,87 @@ void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
}
if ((animData->newState != -1) && (animData->frame == framesCount) &&
- (animData->newState != animData->state)) {
+ (animData->newState != animData->state)) {
+
animData->nextState = animData->newState;
- animData->newState = -1;
- animData->state = animData->nextState;
+ animData->newState = -1;
+ animData->state = animData->nextState;
Scenery::AnimLayer *animLayer =
_vm->_scenery->getAnimLayer(animData->animation, animData->layer);
+
*obj->pPosX += animLayer->animDeltaX;
*obj->pPosY += animLayer->animDeltaY;
- animation = obj->goblinStates[animData->nextState][0].animation;
- layer = obj->goblinStates[animData->nextState][0].layer;
- animData->layer = layer;
+ int16 animation = obj->goblinStates[animData->nextState][0].animation;
+ int16 layer = obj->goblinStates[animData->nextState][0].layer;
+
+ animData->layer = layer;
animData->animation = animation;
- animData->frame = 0;
- } else {
- if (isMovement(animData->state)) {
- state = animData->nextState;
- if (animData->frame == ((framesCount + 1) / 2)) {
- gobX = obj->goblinX;
- gobY = obj->goblinY;
-
- advMovement(obj, state);
-
- if (animData->state != state) {
- animation = obj->goblinStates[state][0].animation;
- layer = obj->goblinStates[state][0].layer;
- animData->layer = layer;
- animData->animation = animation;
- animData->frame = 0;
- animData->state = state;
- _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
- else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
- }
- }
- }
+ animData->frame = 0;
- if (animData->frame >= framesCount) {
- state = animData->nextState;
- animation = obj->goblinStates[state][0].animation;
- layer = obj->goblinStates[state][0].layer;
- animData->layer = layer;
- animData->animation = animation;
- animData->frame = 0;
- animData->state = state;
- gobX = obj->goblinX;
- gobY = obj->goblinY;
+ return;
+ }
+
+ if (isMovement(animData->state)) {
+ int16 state = animData->nextState;
+
+ if (animData->frame == ((framesCount + 1) / 2)) {
+ int16 gobX = obj->goblinX;
+ int16 gobY = obj->goblinY + 1;
advMovement(obj, state);
- _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
- else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
+ if (animData->state != state) {
+ int16 animation = obj->goblinStates[state][0].animation;
+ int16 layer = obj->goblinStates[state][0].layer;
+
+ animData->layer = layer;
+ animData->animation = animation;
+ animData->frame = 0;
+ animData->state = state;
+
+ _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
+ uint32 gobPosX = gobX * _vm->_map->getTilesWidth();
+ uint32 gobPosY = (gobY * _vm->_map->getTilesHeight()) -
+ (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
+
+ if (_vm->_map->hasBigTiles())
+ gobPosY -= gobY / 2;
+
+ *obj->pPosX = gobPosX;
+ *obj->pPosY = gobPosY;
+ }
}
}
+
+ if (animData->frame < framesCount)
+ return;
+
+ int16 state = animData->nextState;
+ int16 animation = obj->goblinStates[state][0].animation;
+ int16 layer = obj->goblinStates[state][0].layer;
+
+ animData->layer = layer;
+ animData->animation = animation;
+ animData->frame = 0;
+ animData->state = state;
+
+ int16 gobX = obj->goblinX;
+ int16 gobY = obj->goblinY + 1;
+
+ advMovement(obj, state);
+
+ _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
+ uint32 gobPosX = gobX * _vm->_map->getTilesWidth();
+ uint32 gobPosY = (gobY * _vm->_map->getTilesHeight()) -
+ (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
+
+ if (_vm->_map->hasBigTiles())
+ gobPosY -= gobY / 2;
+
+ *obj->pPosX = gobPosX;
+ *obj->pPosY = gobPosY;
}
void Goblin_v2::handleGoblins() {
diff --git a/engines/gob/goblin_v4.cpp b/engines/gob/goblin_v4.cpp
index 25c52cef35..523357aab1 100644
--- a/engines/gob/goblin_v4.cpp
+++ b/engines/gob/goblin_v4.cpp
@@ -77,8 +77,12 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
if ((gobX == destX) && (gobY == destY)) {
if (obj->nearestWayPoint > obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -87,8 +91,12 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
obj->nearestWayPoint--;
} else if (obj->nearestWayPoint < obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -98,8 +106,12 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
} else {
if ((_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) == 3) &&
(_vm->_map->getPass(gobDestX, gobDestY) != 0)) {
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
WRITE_VAR(56, 1);
} else {
animData->pathExistence = 1;
@@ -123,20 +135,20 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
obj->destX = destX;
obj->destY = destY;
- if (_vm->_map->_widthByte == 4) {
+ if (_vm->_map->getVersion() == 4) {
switch (dir) {
- case Map::kDirNW:
- animData->nextState = turnState(animData->state, Map::kDirNW);
+ case kDirNW:
+ animData->nextState = turnState(animData->state, kDirNW);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 1))
animData->nextState = 40;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
- animData->nextState = turnState(animData->state, Map::kDirNW);
+ animData->nextState = turnState(animData->state, kDirNW);
break;
- case Map::kDirN:
+ case kDirN:
animData->nextState =
- (animData->curLookDir == 2) ? 2 : turnState(animData->state, Map::kDirN);
+ (animData->curLookDir == 2) ? 2 : turnState(animData->state, kDirN);
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
@@ -154,35 +166,35 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
animData->nextState = 26;
break;
- case Map::kDirNE:
- animData->nextState = turnState(animData->state, Map::kDirNE);
+ case kDirNE:
+ animData->nextState = turnState(animData->state, kDirNE);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 3))
animData->nextState = 42;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
- animData->nextState = turnState(animData->state, Map::kDirNE);
+ animData->nextState = turnState(animData->state, kDirNE);
break;
- case Map::kDirW:
- animData->nextState = turnState(animData->state, Map::kDirW);
+ case kDirW:
+ animData->nextState = turnState(animData->state, kDirW);
break;
- case Map::kDirE:
- animData->nextState = turnState(animData->state, Map::kDirE);
+ case kDirE:
+ animData->nextState = turnState(animData->state, kDirE);
break;
- case Map::kDirSW:
- animData->nextState = turnState(animData->state, Map::kDirSW);
+ case kDirSW:
+ animData->nextState = turnState(animData->state, kDirSW);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 7))
animData->nextState = 41;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY) != 10)
- animData->nextState = turnState(animData->state, Map::kDirSW);
+ animData->nextState = turnState(animData->state, kDirSW);
break;
- case Map::kDirS:
+ case kDirS:
animData->nextState =
- (animData->curLookDir == 6) ? 6 : turnState(animData->state, Map::kDirS);
+ (animData->curLookDir == 6) ? 6 : turnState(animData->state, kDirS);
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY + 2) != 10) {
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY + 2) == 10)
@@ -201,13 +213,13 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
animData->nextState = 27;
break;
- case Map::kDirSE:
- animData->nextState = turnState(animData->state, Map::kDirSE);
+ case kDirSE:
+ animData->nextState = turnState(animData->state, kDirSE);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 5))
animData->nextState = 43;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY) != 10)
- animData->nextState = turnState(animData->state, Map::kDirSE);
+ animData->nextState = turnState(animData->state, kDirSE);
break;
default:
@@ -260,9 +272,9 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
} else {
switch (dir) {
- case Map::kDirNW:
+ case kDirNW:
animData->nextState = 1;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 40;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
@@ -270,10 +282,10 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirN:
+ case kDirN:
animData->nextState =
(animData->curLookDir == 2) ? 2 : rotateState(animData->curLookDir, 2);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
@@ -289,9 +301,9 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirNE:
+ case kDirNE:
animData->nextState = 3;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 42;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
@@ -299,17 +311,17 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirW:
+ case kDirW:
animData->nextState = rotateState(animData->curLookDir, 0);
break;
- case Map::kDirE:
+ case kDirE:
animData->nextState = rotateState(animData->curLookDir, 4);
break;
- case Map::kDirSW:
+ case kDirSW:
animData->nextState = 7;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 41;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY + 2) != 10)
@@ -317,10 +329,10 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirS:
+ case kDirS:
animData->nextState =
(animData->curLookDir == 6) ? 6 : rotateState(animData->curLookDir, 6);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
animData->nextState = 39;
else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
@@ -328,9 +340,9 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirSE:
+ case kDirSE:
animData->nextState = 5;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 43;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY + 2) != 10)
@@ -496,13 +508,13 @@ void Goblin_v4::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
animData->frame = 0;
animData->state = state;
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles())
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
+ *obj->pPosX = gobX * _vm->_map->getTilesWidth();
}
}
}
@@ -521,13 +533,13 @@ void Goblin_v4::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
advMovement(obj, state);
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles())
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
+ *obj->pPosX = gobX * _vm->_map->getTilesWidth();
}
}
}
@@ -589,35 +601,35 @@ int16 Goblin_v4::turnState(int16 state, uint16 dir) {
}
switch (dir) {
- case Map::kDirNW:
+ case kDirNW:
cx = 1;
break;
- case Map::kDirN:
+ case kDirN:
cx = 2;
break;
- case Map::kDirNE:
+ case kDirNE:
cx = 3;
break;
- case Map::kDirW:
+ case kDirW:
cx = 0;
break;
- case Map::kDirE:
+ case kDirE:
cx = 4;
break;
- case Map::kDirSW:
+ case kDirSW:
cx = 7;
break;
- case Map::kDirS:
+ case kDirS:
cx = 6;
break;
- case Map::kDirSE:
+ case kDirSE:
cx = 5;
break;
}
diff --git a/engines/gob/hotspots.cpp b/engines/gob/hotspots.cpp
index dad141a254..94c3c6fb24 100644
--- a/engines/gob/hotspots.cpp
+++ b/engines/gob/hotspots.cpp
@@ -23,9 +23,10 @@
*
*/
+#include "common/str.h"
+
#include "gob/hotspots.h"
#include "gob/global.h"
-#include "gob/helper.h"
#include "gob/draw.h"
#include "gob/game.h"
#include "gob/script.h"
@@ -880,10 +881,10 @@ uint16 Hotspots::updateInput(uint16 xPos, uint16 yPos, uint16 width, uint16 heig
while (1) {
// If we the edit field has enough space, add a space for the new character
- strncpy0(tempStr, str, 254);
+ Common::strlcpy(tempStr, str, 255);
strcat(tempStr, " ");
if ((editSize != 0) && strlen(tempStr) > editSize)
- strncpy0(tempStr, str, 255);
+ Common::strlcpy(tempStr, str, 256);
// Clear input area
fillRect(xPos, yPos,
@@ -2006,14 +2007,14 @@ void Hotspots::checkStringMatch(const Hotspot &spot, const InputDesc &input,
char tempStr[256];
char spotStr[256];
- strncpy0(tempStr, GET_VARO_STR(spot.key), 255);
+ Common::strlcpy(tempStr, GET_VARO_STR(spot.key), 256);
if (spot.getType() < kTypeInput3NoLeave)
_vm->_util->cleanupStr(tempStr);
uint16 pos = 0;
do {
- strncpy0(spotStr, str, 255);
+ Common::strlcpy(spotStr, str, 256);
pos += strlen(str) + 1;
str += strlen(str) + 1;
@@ -2140,7 +2141,7 @@ void Hotspots::updateAllTexts(const InputDesc *inputs) const {
// Get its text
char tempStr[256];
- strncpy0(tempStr, GET_VARO_STR(spot.key), 255);
+ Common::strlcpy(tempStr, GET_VARO_STR(spot.key), 256);
// Coordinates
uint16 x = spot.left;
diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp
index 5c59a5692f..fa209c317f 100644
--- a/engines/gob/init.cpp
+++ b/engines/gob/init.cpp
@@ -57,7 +57,7 @@ void Init::cleanup() {
_vm->_sound->speakerOff();
_vm->_sound->blasterStop(0);
- _vm->_dataIO->closeDataFile();
+ _vm->_dataIO->closeArchive(true);
}
void Init::doDemo() {
@@ -81,17 +81,12 @@ void Init::doDemo() {
}
void Init::initGame() {
- byte *infBuf;
- char *infPtr;
- char *infEnd;
- char buffer[128];
-
initVideo();
updateConfig();
if (!_vm->isDemo()) {
- if (_vm->_dataIO->existData(_vm->_startStk.c_str()))
- _vm->_dataIO->openDataFile(_vm->_startStk.c_str());
+ if (_vm->_dataIO->hasFile(_vm->_startStk))
+ _vm->_dataIO->openArchive(_vm->_startStk, true);
}
_vm->_util->initInput();
@@ -126,37 +121,31 @@ void Init::initGame() {
return;
}
- if (!_vm->_dataIO->existData("intro.inf")) {
+ Common::SeekableReadStream *infFile = _vm->_dataIO->getFile("intro.inf");
+ if (!infFile) {
for (int i = 0; i < 4; i++)
_vm->_draw->loadFont(i, _fontNames[i]);
} else {
- infBuf = _vm->_dataIO->getData("intro.inf");
- infPtr = (char *)infBuf;
-
- infEnd = (char *)(infBuf + _vm->_dataIO->getDataSize("intro.inf"));
-
- for (int i = 0; i < 8; i++, infPtr++) {
- int j;
-
- for (j = 0; infPtr < infEnd && *infPtr >= ' '; j++, infPtr++)
- buffer[j] = *infPtr;
- buffer[j] = 0;
- strcat(buffer, ".let");
-
- _vm->_draw->loadFont(i, buffer);
+ for (int i = 0; i < 8; i++) {
+ if (infFile->eos())
+ break;
- if ((infPtr + 1) >= infEnd)
+ Common::String font = infFile->readLine();
+ if (infFile->eos() && font.empty())
break;
- infPtr++;
+ font += ".let";
+
+ _vm->_draw->loadFont(i, font.c_str());
}
- delete[] infBuf;
+
+ delete infFile;
}
- if (_vm->_dataIO->existData(_vm->_startTot.c_str())) {
+ if (_vm->_dataIO->hasFile(_vm->_startTot)) {
_vm->_inter->allocateVars(Script::getVariablesCount(_vm->_startTot.c_str(), _vm));
strcpy(_vm->_game->_curTotFile, _vm->_startTot.c_str());
@@ -165,7 +154,7 @@ void Init::initGame() {
_vm->_sound->cdLoadLIC("gob.lic");
// Search for a Coktel logo animation or image to display
- if (_vm->_dataIO->existData("coktel.imd")) {
+ if (_vm->_dataIO->hasFile("coktel.imd")) {
_vm->_draw->initScreen();
_vm->_draw->_cursorIndex = -1;
@@ -179,26 +168,28 @@ void Init::initGame() {
}
_vm->_draw->closeScreen();
- } else if (_vm->_dataIO->existData("coktel.clt")) {
- _vm->_draw->initScreen();
- _vm->_util->clearPalette();
-
- DataStream *stream = _vm->_dataIO->getDataStream("coktel.clt");
- stream->read((byte *)_vm->_draw->_vgaPalette, 768);
- delete stream;
-
- if (_vm->_dataIO->existData("coktel.ims")) {
- byte *sprBuf;
-
- sprBuf = _vm->_dataIO->getData("coktel.ims");
- _vm->_video->drawPackedSprite(sprBuf, 320, 200, 0, 0, 0,
- *_vm->_draw->_frontSurface);
- _vm->_palAnim->fade(_palDesc, 0, 0);
- _vm->_util->delay(500);
-
- delete[] sprBuf;
+ } else if (_vm->_dataIO->hasFile("coktel.clt")) {
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile("coktel.clt");
+ if (stream) {
+ _vm->_draw->initScreen();
+ _vm->_util->clearPalette();
+
+ stream->read((byte *)_vm->_draw->_vgaPalette, 768);
+ delete stream;
+
+ int32 size;
+ byte *sprite = _vm->_dataIO->getFile("coktel.ims", size);
+ if (sprite) {
+ _vm->_video->drawPackedSprite(sprite, 320, 200, 0, 0, 0,
+ *_vm->_draw->_frontSurface);
+ _vm->_palAnim->fade(_palDesc, 0, 0);
+ _vm->_util->delay(500);
+
+ delete[] sprite;
+ }
+
+ _vm->_draw->closeScreen();
}
- _vm->_draw->closeScreen();
}
_vm->_game->start();
@@ -209,7 +200,7 @@ void Init::initGame() {
}
delete _palDesc;
- _vm->_dataIO->closeDataFile();
+ _vm->_dataIO->closeArchive(true);
_vm->_video->initPrimary(-1);
cleanup();
}
diff --git a/engines/gob/inter.cpp b/engines/gob/inter.cpp
index f6e6d41100..8db42b217c 100644
--- a/engines/gob/inter.cpp
+++ b/engines/gob/inter.cpp
@@ -268,23 +268,23 @@ void Inter::funcBlock(int16 retFlag) {
int addr = _vm->_game->_script->pos();
if ((startaddr == 0x18B4 && addr == 0x1A7F && // Zombie, EGA
- !strncmp(_vm->_game->_curTotFile, "avt005.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt005.tot")) ||
(startaddr == 0x188D && addr == 0x1A58 && // Zombie, Mac
- !strncmp(_vm->_game->_curTotFile, "avt005.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt005.tot")) ||
(startaddr == 0x1299 && addr == 0x139A && // Dungeon
- !strncmp(_vm->_game->_curTotFile, "avt006.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt006.tot")) ||
(startaddr == 0x11C0 && addr == 0x12C9 && // Cauldron, EGA
- !strncmp(_vm->_game->_curTotFile, "avt012.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt012.tot")) ||
(startaddr == 0x11C8 && addr == 0x1341 && // Cauldron, Mac
- !strncmp(_vm->_game->_curTotFile, "avt012.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt012.tot")) ||
(startaddr == 0x09F2 && addr == 0x0AF3 && // Statue
- !strncmp(_vm->_game->_curTotFile, "avt016.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt016.tot")) ||
(startaddr == 0x0B92 && addr == 0x0C93 && // Castle
- !strncmp(_vm->_game->_curTotFile, "avt019.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt019.tot")) ||
(startaddr == 0x17D9 && addr == 0x18DA && // Finale, EGA
- !strncmp(_vm->_game->_curTotFile, "avt022.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt022.tot")) ||
(startaddr == 0x17E9 && addr == 0x19A8 && // Finale, Mac
- !strncmp(_vm->_game->_curTotFile, "avt022.tot", 10))) {
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt022.tot"))) {
_vm->_util->longDelay(5000);
}
@@ -295,7 +295,7 @@ void Inter::funcBlock(int16 retFlag) {
// of Fascination have a too short delay between the storage room and the lab.
// We manually add it here.
if ((_vm->getGameType() == kGameTypeFascination) &&
- !strncmp(_vm->_game->_curTotFile, "PLANQUE.tot", 9)) {
+ !scumm_stricmp(_vm->_game->_curTotFile, "PLANQUE.tot")) {
int addr = _vm->_game->_script->pos();
if ((startaddr == 0x0202 && addr == 0x0330) || // Before Lab, Amiga & Atari, English
(startaddr == 0x023D && addr == 0x032D) || // Before Lab, PC floppy, German
@@ -306,6 +306,21 @@ void Inter::funcBlock(int16 retFlag) {
} // End of workaround
cmd = _vm->_game->_script->readByte();
+
+ // WORKAROUND:
+ // A VGA version has some broken code in its scripts, this workaround skips the corrupted parts.
+ if (_vm->getGameType() == kGameTypeFascination) {
+ int addr = _vm->_game->_script->pos();
+ if ((startaddr == 0x212D) && (addr == 0x290E) && (cmd == 0x90) && !scumm_stricmp(_vm->_game->_curTotFile, "INTRO1.tot")) {
+ _vm->_game->_script->skip(2);
+ cmd = _vm->_game->_script->readByte();
+ }
+ if ((startaddr == 0x207D) && (addr == 0x22CE) && (cmd == 0x90) && !scumm_stricmp(_vm->_game->_curTotFile, "INTRO2.tot")) {
+ _vm->_game->_script->skip(2);
+ cmd = _vm->_game->_script->readByte();
+ }
+ }
+
if ((cmd >> 4) >= 12) {
cmd2 = 16 - (cmd >> 4);
cmd &= 0xF;
diff --git a/engines/gob/inter_bargon.cpp b/engines/gob/inter_bargon.cpp
index 3afb70d6c0..5ed24c614e 100644
--- a/engines/gob/inter_bargon.cpp
+++ b/engines/gob/inter_bargon.cpp
@@ -175,10 +175,12 @@ void Inter_Bargon::oBargon_intro3(OpGobParams &params) {
static const char *sndFiles[] = {"1INTROIV.snd", "2INTROIV.snd"};
static const char *palFiles[] = {"2ou2.clt", "2ou3.clt", "2ou4.clt", "2ou5.clt"};
+ int32 size;
+
for (int i = 0; i < 2; i++)
_vm->_sound->sampleLoad(&samples[i], SOUND_SND, sndFiles[i]);
for (int i = 0; i < 4; i++)
- palettes[i] = _vm->_dataIO->getData(palFiles[i]);
+ palettes[i] = _vm->_dataIO->getFile(palFiles[i], size);
palBak = _vm->_global->_pPaletteDesc->vgaPal;
_vm->_sound->blasterPlayComposition(comp, 0, samples, 2);
diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp
index befed4b1c2..05032d712c 100644
--- a/engines/gob/inter_playtoons.cpp
+++ b/engines/gob/inter_playtoons.cpp
@@ -24,12 +24,12 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gui/message.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
@@ -246,10 +246,10 @@ bool Inter_Playtoons::oPlaytoons_checkData(OpFuncParams &params) {
mode = _vm->_saveLoad->getSaveMode(file);
if (mode == SaveLoad::kSaveModeNone) {
- if (_vm->_dataIO->existData(file))
- size = _vm->_dataIO->getDataSize(file);
- else
+ size = _vm->_dataIO->fileSize(file);
+ if (size == -1)
warning("File \"%s\" not found", file);
+
} else if (mode == SaveLoad::kSaveModeSave)
size = _vm->_saveLoad->getSize(file);
else if (mode == SaveLoad::kSaveModeExists)
@@ -272,7 +272,6 @@ bool Inter_Playtoons::oPlaytoons_readData(OpFuncParams &params) {
int32 size;
int32 offset;
uint16 dataVar;
- int16 handle;
byte *buf;
SaveLoad::SaveMode mode;
@@ -329,13 +328,10 @@ bool Inter_Playtoons::oPlaytoons_readData(OpFuncParams &params) {
}
WRITE_VAR(1, 1);
- handle = _vm->_dataIO->openData(file);
-
- if (handle < 0)
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(file);
+ if (!stream)
return false;
- DataStream *stream = _vm->_dataIO->openAsStream(handle, true);
-
_vm->_draw->animateCursor(4);
if (offset < 0)
stream->seek(offset + 1, SEEK_END);
@@ -415,9 +411,9 @@ void Inter_Playtoons::oPlaytoons_copyFile() {
char fileName2[128];
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName1, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName1, _vm->_game->_script->getResultStr(), 128);
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName2, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName2, _vm->_game->_script->getResultStr(), 128);
warning("Playtoons Stub: copy file from \"%s\" to \"%s\"", fileName1, fileName2);
}
@@ -427,7 +423,7 @@ void Inter_Playtoons::oPlaytoons_openItk() {
char *backSlash;
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 124);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 124);
if (!strchr(fileName, '.'))
strcat(fileName, ".ITK");
@@ -435,9 +431,9 @@ void Inter_Playtoons::oPlaytoons_openItk() {
// Workaround for Bambou : In the script, the path is hardcoded (!!)
if ((backSlash = strrchr(fileName, '\\'))) {
debugC(2, kDebugFileIO, "Opening ITK file \"%s\" instead of \"%s\"", backSlash + 1, fileName);
- _vm->_dataIO->openDataFile(backSlash + 1, true);
+ _vm->_dataIO->openArchive(backSlash + 1, false);
} else
- _vm->_dataIO->openDataFile(fileName, true);
+ _vm->_dataIO->openArchive(fileName, false);
// All the other checks are meant to verify (if not found at the first try)
// if the file is present on the CD or not. As everything is supposed to
// be copied, those checks are skipped
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index 3bf7fc8fd4..933bb2f2ed 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/file.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
@@ -812,12 +812,12 @@ bool Inter_v1::o1_if(OpFuncParams &params) {
byte cmd;
bool boolRes;
- // WORKAROUND: Windows Gob1 OUTODDV reload goblin stuck bug present in original
- if ((_vm->getGameType() == kGameTypeGob1) && (_vm->_game->_script->pos() == 11294) &&
- !scumm_stricmp(_vm->_game->_curTotFile, "avt00.tot") && VAR(59) == 1) {
- warning("Workaround for Win Gob1 OUTODDV Reload Goblin Stuck Bug...");
+ // WORKAROUND: Gob1 goblin stuck on reload bugs present in original - bugs #3018918 and 3065914
+ if ((_vm->getGameType() == kGameTypeGob1) && (_vm->_game->_script->pos() == 2933) &&
+ !scumm_stricmp(_vm->_game->_curTotFile, "inter.tot") && VAR(285) != 0) {
+ warning("Workaround for Gob1 Goblin Stuck On Reload Bug applied...");
+ // VAR(59) actually locks goblin movement, but these variables trigger this in the script.
WRITE_VAR(285, 0);
- WRITE_VAR(59, 0);
}
boolRes = _vm->_game->_script->evalBoolResult();
@@ -971,7 +971,7 @@ bool Inter_v1::o1_loadTot(OpFuncParams &params) {
if ((_vm->_game->_script->peekByte() & 0x80) != 0) {
_vm->_game->_script->skip(1);
_vm->_game->_script->evalExpr(0);
- strncpy0(buf, _vm->_game->_script->getResultStr(), 15);
+ Common::strlcpy(buf, _vm->_game->_script->getResultStr(), 16);
} else {
size = _vm->_game->_script->readInt8();
memcpy(buf, _vm->_game->_script->readString(size), size);
@@ -1512,7 +1512,7 @@ bool Inter_v1::o1_strToLong(OpFuncParams &params) {
int32 res;
strVar = _vm->_game->_script->readVarIndex();
- strncpy0(str, GET_VARO_STR(strVar), 19);
+ Common::strlcpy(str, GET_VARO_STR(strVar), 20);
res = atoi(str);
destVar = _vm->_game->_script->readVarIndex();
@@ -1630,18 +1630,17 @@ bool Inter_v1::o1_getFreeMem(OpFuncParams &params) {
}
bool Inter_v1::o1_checkData(OpFuncParams &params) {
- int16 handle;
int16 varOff;
_vm->_game->_script->evalExpr(0);
varOff = _vm->_game->_script->readVarIndex();
- handle = _vm->_dataIO->openData(_vm->_game->_script->getResultStr());
- WRITE_VAR_OFFSET(varOff, handle);
- if (handle >= 0)
- _vm->_dataIO->closeData(handle);
- else
+ if (!_vm->_dataIO->hasFile(_vm->_game->_script->getResultStr())) {
warning("File \"%s\" not found", _vm->_game->_script->getResultStr());
+ WRITE_VAR_OFFSET(varOff, (uint32) -1);
+ } else
+ WRITE_VAR_OFFSET(varOff, 50); // "handle" between 50 and 128 = in archive
+
return false;
}
@@ -1767,7 +1766,6 @@ bool Inter_v1::o1_readData(OpFuncParams &params) {
int16 size;
int16 dataVar;
int16 offset;
- int16 handle;
_vm->_game->_script->evalExpr(0);
dataVar = _vm->_game->_script->readVarIndex();
@@ -1776,26 +1774,26 @@ bool Inter_v1::o1_readData(OpFuncParams &params) {
retSize = 0;
WRITE_VAR(1, 1);
- handle = _vm->_dataIO->openData(_vm->_game->_script->getResultStr());
- if (handle >= 0) {
- DataStream *stream = _vm->_dataIO->openAsStream(handle, true);
- _vm->_draw->animateCursor(4);
- if (offset < 0)
- stream->seek(offset + 1, SEEK_END);
- else
- stream->seek(offset);
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(_vm->_game->_script->getResultStr());
+ if (!stream)
+ return false;
- if (((dataVar >> 2) == 59) && (size == 4))
- WRITE_VAR(59, stream->readUint32LE());
- else
- retSize = stream->read((byte *)_variables->getAddressOff8(dataVar), size);
+ _vm->_draw->animateCursor(4);
+ if (offset < 0)
+ stream->seek(offset + 1, SEEK_END);
+ else
+ stream->seek(offset);
- if (retSize == size)
- WRITE_VAR(1, 0);
+ if (((dataVar >> 2) == 59) && (size == 4))
+ WRITE_VAR(59, stream->readUint32LE());
+ else
+ retSize = stream->read((byte *)_variables->getAddressOff8(dataVar), size);
- delete stream;
- }
+ if (retSize == size)
+ WRITE_VAR(1, 0);
+
+ delete stream;
return false;
}
@@ -1824,9 +1822,9 @@ bool Inter_v1::o1_manageDataFile(OpFuncParams &params) {
_vm->_game->_script->evalExpr(0);
if (_vm->_game->_script->getResultStr()[0] != 0)
- _vm->_dataIO->openDataFile(_vm->_game->_script->getResultStr());
+ _vm->_dataIO->openArchive(_vm->_game->_script->getResultStr(), true);
else
- _vm->_dataIO->closeDataFile();
+ _vm->_dataIO->closeArchive(true);
return false;
}
@@ -2562,8 +2560,8 @@ void Inter_v1::animPalette() {
}
void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
- for (int y = 0; y < _vm->_map->_mapHeight; y++) {
- for (int x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (int y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (int x = 0; x < _vm->_map->getMapWidth(); x++) {
if ((_vm->_map->getItem(x, y) & 0xFF) == item)
_vm->_map->setItem(x, y, _vm->_map->getItem(x, y) & 0xFF00);
else if (((_vm->_map->getItem(x, y) & 0xFF00) >> 8) == item)
@@ -2571,7 +2569,7 @@ void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
}
}
- if (xPos < _vm->_map->_mapWidth - 1) {
+ if (xPos < _vm->_map->getMapWidth() - 1) {
if (yPos > 0) {
if (((_vm->_map->getItem(xPos, yPos) & 0xFF00) != 0) ||
((_vm->_map->getItem(xPos, yPos - 1) & 0xFF00) != 0) ||
@@ -2660,7 +2658,7 @@ void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
return;
}
- if ((xPos < _vm->_map->_mapWidth - 2) &&
+ if ((xPos < _vm->_map->getMapWidth() - 2) &&
(_vm->_map->getPass(xPos + 2, yPos) == 1)) {
_vm->_map->_itemPoses[item].x = xPos + 2;
_vm->_map->_itemPoses[item].y = yPos;
@@ -2668,7 +2666,7 @@ void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
return;
}
- if ((xPos < _vm->_map->_mapWidth - 1) &&
+ if ((xPos < _vm->_map->getMapWidth() - 1) &&
(_vm->_map->getPass(xPos + 1, yPos) == 1)) {
_vm->_map->_itemPoses[item].x = xPos + 1;
_vm->_map->_itemPoses[item].y = yPos;
diff --git a/engines/gob/inter_v2.cpp b/engines/gob/inter_v2.cpp
index 0003332e47..d8d36d7a41 100644
--- a/engines/gob/inter_v2.cpp
+++ b/engines/gob/inter_v2.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gui/message.h"
@@ -32,7 +33,6 @@
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
@@ -432,7 +432,7 @@ void Inter_v2::o2_loadMultObject() {
obj.gobDestY = val;
obj.goblinY = val;
- *(obj.pPosX) *= _vm->_map->_tilesWidth;
+ *(obj.pPosX) *= _vm->_map->getTilesWidth();
layer = objAnim.layer;
animation = obj.goblinStates[layer][0].animation;
@@ -447,14 +447,14 @@ void Inter_v2::o2_loadMultObject() {
_vm->_scenery->updateAnim(layer, 0, animation, 0,
*(obj.pPosX), *(obj.pPosY), 0);
- if (!_vm->_map->_bigTiles)
- *(obj.pPosY) = (obj.goblinY + 1) * _vm->_map->_tilesHeight
+ if (!_vm->_map->hasBigTiles())
+ *(obj.pPosY) = (obj.goblinY + 1) * _vm->_map->getTilesHeight()
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
else
- *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->_tilesHeight) -
+ *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
((obj.goblinY + 1) / 2);
- *(obj.pPosX) = obj.goblinX * _vm->_map->_tilesWidth;
+ *(obj.pPosX) = obj.goblinX * _vm->_map->getTilesWidth();
} else if ((objAnim.animType == 101) && (objIndex < _vm->_goblin->_gobsCount)) {
@@ -530,7 +530,7 @@ void Inter_v2::o2_readLIC() {
char path[40];
_vm->_game->_script->evalExpr(0);
- strncpy0(path, _vm->_game->_script->getResultStr(), 35);
+ Common::strlcpy(path, _vm->_game->_script->getResultStr(), 36);
strcat(path, ".LIC");
_vm->_sound->cdLoadLIC(path);
@@ -778,14 +778,14 @@ void Inter_v2::o2_setGoblinState() {
_vm->_scenery->updateAnim(layer, 0, animation, 0,
*(obj.pPosX), *(obj.pPosY), 0);
- if (_vm->_map->_bigTiles)
- *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles())
+ *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
((obj.goblinY + 1) / 2);
else
- *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->_tilesHeight) -
+ *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *(obj.pPosX) = obj.goblinX * _vm->_map->_tilesWidth;
+ *(obj.pPosX) = obj.goblinX * _vm->_map->getTilesWidth();
break;
}
}
@@ -963,7 +963,7 @@ void Inter_v2::o2_playImd() {
_vm->_game->_script->evalExpr(0);
_vm->_game->_script->getResultStr()[8] = 0;
- strncpy0(imd, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(imd, _vm->_game->_script->getResultStr(), 128);
VideoPlayer::Properties props;
@@ -1031,15 +1031,15 @@ void Inter_v2::o2_openItk() {
char fileName[32];
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 27);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 28);
if (!strchr(fileName, '.'))
strcat(fileName, ".ITK");
- _vm->_dataIO->openDataFile(fileName, true);
+ _vm->_dataIO->openArchive(fileName, false);
}
void Inter_v2::o2_closeItk() {
- _vm->_dataIO->closeDataFile(true);
+ _vm->_dataIO->closeArchive(false);
}
void Inter_v2::o2_setImdFrontSurf() {
@@ -1292,7 +1292,6 @@ bool Inter_v2::o2_getFreeMem(OpFuncParams &params) {
}
bool Inter_v2::o2_checkData(OpFuncParams &params) {
- int16 handle;
int16 varOff;
int32 size;
SaveLoad::SaveMode mode;
@@ -1301,7 +1300,6 @@ bool Inter_v2::o2_checkData(OpFuncParams &params) {
varOff = _vm->_game->_script->readVarIndex();
size = -1;
- handle = 1;
char *file = _vm->_game->_script->getResultStr();
@@ -1313,9 +1311,8 @@ bool Inter_v2::o2_checkData(OpFuncParams &params) {
mode = _vm->_saveLoad->getSaveMode(file);
if (mode == SaveLoad::kSaveModeNone) {
- if (_vm->_dataIO->existData(file))
- size = _vm->_dataIO->getDataSize(file);
- else
+ size = _vm->_dataIO->fileSize(file);
+ if (size == -1)
warning("File \"%s\" not found", file);
} else if (mode == SaveLoad::kSaveModeSave)
@@ -1323,13 +1320,10 @@ bool Inter_v2::o2_checkData(OpFuncParams &params) {
else if (mode == SaveLoad::kSaveModeExists)
size = 23;
- if (size == -1)
- handle = -1;
-
debugC(2, kDebugFileIO, "Requested size of file \"%s\": %d",
file, size);
- WRITE_VAR_OFFSET(varOff, handle);
+ WRITE_VAR_OFFSET(varOff, (size == -1) ? -1 : 50);
WRITE_VAR(16, (uint32) size);
return false;
@@ -1340,7 +1334,6 @@ bool Inter_v2::o2_readData(OpFuncParams &params) {
int32 size;
int32 offset;
int16 dataVar;
- int16 handle;
byte *buf;
SaveLoad::SaveMode mode;
@@ -1391,13 +1384,10 @@ bool Inter_v2::o2_readData(OpFuncParams &params) {
}
WRITE_VAR(1, 1);
- handle = _vm->_dataIO->openData(file);
-
- if (handle < 0)
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(file);
+ if (!file)
return false;
- DataStream *stream = _vm->_dataIO->openAsStream(handle, true);
-
_vm->_draw->animateCursor(4);
if (offset < 0)
stream->seek(offset + 1, SEEK_END);
@@ -1462,7 +1452,7 @@ void Inter_v2::o2_loadInfogramesIns(OpGobParams &params) {
varName = _vm->_game->_script->readInt16();
- strncpy0(fileName, GET_VAR_STR(varName), 15);
+ Common::strlcpy(fileName, GET_VAR_STR(varName), 16);
strcat(fileName, ".INS");
_vm->_sound->infogramesLoadInstruments(fileName);
@@ -1474,7 +1464,7 @@ void Inter_v2::o2_playInfogrames(OpGobParams &params) {
varName = _vm->_game->_script->readInt16();
- strncpy0(fileName, GET_VAR_STR(varName), 15);
+ Common::strlcpy(fileName, GET_VAR_STR(varName), 16);
strcat(fileName, ".DUM");
_vm->_sound->infogramesLoadSong(fileName);
@@ -1512,16 +1502,13 @@ void Inter_v2::o2_handleGoblins(OpGobParams &params) {
}
int16 Inter_v2::loadSound(int16 search) {
- byte *dataPtr;
int16 id;
int16 slot;
uint16 slotIdMask;
- uint32 dataSize;
SoundType type;
type = SOUND_SND;
slotIdMask = 0;
- dataSize = 0;
if (!search) {
slot = _vm->_game->_script->readValExpr();
@@ -1560,15 +1547,15 @@ int16 Inter_v2::loadSound(int16 search) {
if (id == -1) {
char sndfile[14];
- strncpy0(sndfile, _vm->_game->_script->readString(9), 9);
+ Common::strlcpy(sndfile, _vm->_game->_script->readString(9), 10);
if (type == SOUND_ADL)
strcat(sndfile, ".ADL");
else
strcat(sndfile, ".SND");
- dataPtr = _vm->_dataIO->getData(sndfile);
- dataSize = _vm->_dataIO->getDataSize(sndfile);
+ int32 dataSize;
+ byte *dataPtr = _vm->_dataIO->getFile(sndfile, dataSize);
if (!dataPtr)
return 0;
diff --git a/engines/gob/inter_v4.cpp b/engines/gob/inter_v4.cpp
index 26eff4f675..698dddeae9 100644
--- a/engines/gob/inter_v4.cpp
+++ b/engines/gob/inter_v4.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/file.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/draw.h"
#include "gob/game.h"
@@ -145,7 +145,7 @@ void Inter_v4::o4_playVmdOrMusic() {
bool close;
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 128);
// WORKAROUND: The nut rolling animation in the administration center
// in Woodruff is called "noixroul", but the scripts think it's "noixroule".
diff --git a/engines/gob/inter_v6.cpp b/engines/gob/inter_v6.cpp
index c6d0211c8f..b6884c6fbe 100644
--- a/engines/gob/inter_v6.cpp
+++ b/engines/gob/inter_v6.cpp
@@ -24,12 +24,12 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/file.h"
#include "graphics/dither.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/game.h"
@@ -104,7 +104,7 @@ void Inter_v6::o6_playVmdOrMusic() {
bool close;
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 128);
VideoPlayer::Properties props;
@@ -187,20 +187,20 @@ void Inter_v6::o6_openItk() {
char fileName[32];
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 27);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 28);
if (!strchr(fileName, '.'))
strcat(fileName, ".ITK");
- _vm->_dataIO->openDataFile(fileName, true);
+ _vm->_dataIO->openArchive(fileName, false);
// WORKAROUND: The CD number detection in Urban Runner is quite daft
// (it checks CD1.ITK - CD4.ITK and the first that's found determines
// the CD number), while its NO_CD modus wants everything in CD1.ITK.
// So we just open the other ITKs, too.
if (_vm->_global->_noCd && !scumm_stricmp(fileName, "CD1.ITK")) {
- _vm->_dataIO->openDataFile("CD2.ITK", true);
- _vm->_dataIO->openDataFile("CD3.ITK", true);
- _vm->_dataIO->openDataFile("CD4.ITK", true);
+ _vm->_dataIO->openArchive("CD2.ITK", false);
+ _vm->_dataIO->openArchive("CD3.ITK", false);
+ _vm->_dataIO->openArchive("CD4.ITK", false);
}
}
@@ -439,7 +439,7 @@ void Inter_v6::probe16bitMusic(char *fileName) {
fileName[len - 1] = 'V';
- if (_vm->_dataIO->existData(fileName))
+ if (_vm->_dataIO->hasFile(fileName))
return;
fileName[len - 1] = '8';
diff --git a/engines/gob/map.cpp b/engines/gob/map.cpp
index 9f1f5bdc59..500f6515ec 100644
--- a/engines/gob/map.cpp
+++ b/engines/gob/map.cpp
@@ -32,33 +32,40 @@
namespace Gob {
Map::Map(GobEngine *vm) : _vm(vm) {
- _widthByte = 0;
- _mapWidth = -1;
+ _mapVersion = 0;
+
+ _passWidth = 0;
+ _mapWidth = -1;
_mapHeight = -1;
- _screenWidth = 0;
+ _passMap = 0;
+
+ _screenWidth = 0;
_screenHeight = 0;
- _tilesWidth = 0;
+
+ _tilesWidth = 0;
_tilesHeight = 0;
- _passWidth = 0;
- _passMap = 0;
- _itemsMap = 0;
- _wayPointsCount = 0;
- _wayPoints = 0;
_bigTiles = false;
+ _wayPointCount = 0;
+ _wayPoints = 0;
+
+ _nearestWayPoint = 0;
+ _nearestDest = 0;
+
+ _itemsMap = 0;
+
for (int i = 0; i < 40; i++) {
- _itemPoses[i].x = 0;
- _itemPoses[i].y = 0;
+ _itemPoses[i].x = 0;
+ _itemPoses[i].y = 0;
_itemPoses[i].orient = 0;
}
- _nearestWayPoint = 0;
- _nearestDest = 0;
_curGoblinX = 0;
_curGoblinY = 0;
_destX = 0;
_destY = 0;
+
_sourceFile[0] = 0;
_loadFromAvo = false;
@@ -76,6 +83,87 @@ Map::~Map() {
delete[] _wayPoints;
}
+uint8 Map::getVersion() const {
+ return _mapVersion;
+}
+
+int16 Map::getMapWidth() const {
+ return _mapWidth;
+}
+
+int16 Map::getMapHeight() const {
+ return _mapHeight;
+}
+
+int16 Map::getScreenWidth() const {
+ return _screenWidth;
+}
+
+int16 Map::getScreenHeight() const {
+ return _screenHeight;
+}
+
+int16 Map::getTilesWidth() const {
+ return _tilesWidth;
+}
+
+int16 Map::getTilesHeight() const {
+ return _tilesHeight;
+}
+
+bool Map::hasBigTiles() const {
+ return _bigTiles;
+}
+
+int8 Map::getPass(int x, int y, int width) const {
+ if (!_passMap)
+ return 0;
+
+ if ((x < 0) || (y < 0) || (x >= _mapWidth) || (y >= _mapHeight))
+ return 0;
+
+ if (width == -1)
+ width = _passWidth;
+ return _passMap[y * width + x];
+}
+
+void Map::setPass(int x, int y, int8 pass, int width) {
+ if (!_passMap)
+ return;
+
+ if ((x < 0) || (y < 0) || (x >= _mapWidth) || (y >= _mapHeight))
+ return;
+
+ if (width == -1)
+ width = _passWidth;
+ _passMap[y * width + x] = pass;
+}
+
+const WayPoint &Map::getWayPoint(int n) const {
+ assert(_wayPoints);
+ assert(n < _wayPointCount);
+
+ return _wayPoints[n];
+}
+
+int16 Map::getItem(int x, int y) const {
+ assert(_itemsMap);
+
+ x = CLIP<int>(x, 0, _mapWidth - 1);
+ y = CLIP<int>(y, 0, _mapHeight - 1);
+
+ return _itemsMap[y][x];
+}
+
+void Map::setItem(int x, int y, int16 item) {
+ assert(_itemsMap);
+
+ x = CLIP<int>(x, 0, _mapWidth - 1);
+ y = CLIP<int>(y, 0, _mapHeight - 1);
+
+ _itemsMap[y][x] = item;
+}
+
void Map::placeItem(int16 x, int16 y, int16 id) {
if ((getItem(x, y) & 0xFF00) != 0)
setItem(x, y, (getItem(x, y) & 0xFF00) | id);
@@ -83,150 +171,175 @@ void Map::placeItem(int16 x, int16 y, int16 id) {
setItem(x, y, (getItem(x, y) & 0x00FF) | (id << 8));
}
-enum {
- kLeft = (1 << 0),
- kUp = (1 << 1),
- kRight = (1 << 2),
- kDown = (1 << 3)
-};
-
-int16 Map::getDirection(int16 x0, int16 y0, int16 x1, int16 y1) {
- int16 dir = 0;
-
+Direction Map::getDirection(int16 x0, int16 y0, int16 x1, int16 y1) {
if ((x0 == x1) && (y0 == y1))
- return 0;
+ // Already at the destination
+ return kDirNone;
if ((x1 < 0) || (x1 > _mapWidth) || (y1 < 0) || (y1 > _mapHeight))
- return 0;
+ // Destination out of range
+ return kDirNone;
+ RelativeDirection relDir = kRelDirNone;
+
+ // Find the direct direction we want to move
if (y1 > y0)
- dir |= kDown;
+ relDir = kRelDirDown;
else if (y1 < y0)
- dir |= kUp;
+ relDir = kRelDirUp;
if (x1 > x0)
- dir |= kRight;
+ relDir = (RelativeDirection)(relDir | kRelDirRight);
else if (x1 < x0)
- dir |= kLeft;
+ relDir = (RelativeDirection)(relDir | kRelDirLeft);
- if ((getPass(x0, y0) == 3) && (dir & kUp)) {
- if ((getPass(x0, y0 - 1) != 0))
- return kDirN;
- }
- if ((getPass(x0, y0) == 3) && (dir & kDown)) {
- if ((getPass(x0, y0 + 1) != 0))
- return kDirS;
- }
+ // Are we on ladders and can continue the ladder in the wanted direction?
+ if ((getPass(x0, y0) == 3) && (relDir & kRelDirUp ) && (getPass(x0, y0 - 1) != 0))
+ return kDirN;
- if ((getPass(x0, y0) == 6) && (dir & kUp)) {
- if ((getPass(x0, y0 - 1) != 0))
- return kDirN;
- }
+ if ((getPass(x0, y0) == 3) && (relDir & kRelDirDown) && (getPass(x0, y0 + 1) != 0))
+ return kDirS;
- if ((getPass(x0, y0) == 6) && (dir & kDown)) {
- if ((getPass(x0, y0 + 1) != 0))
- return kDirS;
- }
+ if ((getPass(x0, y0) == 6) && (relDir & kRelDirUp ) && (getPass(x0, y0 - 1) != 0))
+ return kDirN;
- if (dir == kLeft) {
- if (((x0 - 1) >= 0) && (getPass(x0 - 1, y0) != 0))
+ if ((getPass(x0, y0) == 6) && (relDir & kRelDirDown) && (getPass(x0, y0 + 1) != 0))
+ return kDirS;
+
+
+ // Want to go left
+ if (relDir == kRelDirLeft) {
+ if (getPass(x0 - 1, y0) != 0)
+ // Can go west
return kDirW;
- return 0;
+
+ // Can't go
+ return kDirNone;
}
- if (dir == kRight) {
- if (((x0 + 1) < _mapWidth) && (getPass(x0 + 1, y0) != 0))
+ // Want to go left
+ if (relDir == kRelDirRight) {
+ if (getPass(x0 + 1, y0) != 0)
+ // Can go east
return kDirE;
- return 0;
+
+ // Can't go
+ return kDirNone;
}
- if (dir == kUp) {
- if (((y0 - 1) >= 0) && (getPass(x0, y0 - 1) != 0))
+
+ // Want to go up
+ if (relDir == kRelDirUp) {
+ if (getPass(x0 , y0 - 1) != 0)
+ // Can go north
return kDirN;
- if (((y0 - 1) >= 0) && ((x0 - 1) >= 0) &&
- (getPass(x0 - 1, y0 - 1) != 0))
+ if (getPass(x0 - 1, y0 - 1) != 0)
+ // Can up north-west instead
return kDirNW;
- if (((y0 - 1) >= 0) && ((x0 + 1) < _mapWidth) &&
- (getPass(x0 + 1, y0 - 1) != 0))
+ if (getPass(x0 + 1, y0 - 1) != 0)
+ // Can up north-east instead
return kDirNE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == kDown) {
- if (((y0 + 1) < _mapHeight) && (getPass(x0, y0 + 1) != 0))
+ // Want to go down
+ if (relDir == kRelDirDown) {
+ if (getPass(x0 , y0 + 1) != 0)
+ // Can go south
return kDirS;
- if (((y0 + 1) < _mapHeight) && ((x0 - 1) >= 0) &&
- (getPass(x0 - 1, y0 + 1) != 0))
+ if (getPass(x0 - 1, y0 + 1) != 0)
+ // Can up south-west instead
return kDirSW;
- if (((y0 + 1) < _mapHeight) && ((x0 + 1) < _mapWidth) &&
- (getPass(x0 + 1, y0 + 1) != 0))
+ if (getPass(x0 + 1, y0 + 1) != 0)
+ // Can up south-east instead
return kDirSE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kRight | kUp)) {
- if (((y0 - 1) >= 0) && ((x0 + 1) < _mapWidth) &&
- (getPass(x0 + 1, y0 - 1) != 0))
+
+ // Want to go up and right
+ if (relDir == kRelDirRightUp) {
+ if (getPass(x0 + 1, y0 - 1) != 0)
+ // Can go north-east
return kDirNE;
- if (((y0 - 1) >= 0) && (getPass(x0, y0 - 1) != 0))
+ if (getPass(x0 , y0 - 1) != 0)
+ // Can only go north
return kDirN;
- if (((x0 + 1) < _mapWidth) && (getPass(x0 + 1, y0) != 0))
+ if (getPass(x0 + 1, y0 ) != 0)
+ // Can only go east
return kDirE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kRight | kDown)) {
- if (((x0 + 1) < _mapWidth) && ((y0 + 1) < _mapHeight) &&
- (getPass(x0 + 1, y0 + 1) != 0))
+ // Want to go down and right
+ if (relDir == kRelDirRightDown) {
+ if (getPass(x0 + 1, y0 + 1) != 0)
+ // Can go south-east
return kDirSE;
- if (((y0 + 1) < _mapHeight) && (getPass(x0, y0 + 1) != 0))
+ if (getPass(x0 , y0 + 1) != 0)
+ // Can only go south
return kDirS;
- if (((x0 + 1) < _mapWidth) && (getPass(x0 + 1, y0) != 0))
+ if (getPass(x0 + 1, y0 ) != 0)
+ // Can only go east
return kDirE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kLeft | kUp)) {
- if (((x0 - 1) >= 0) && ((y0 - 1) >= 0) &&
- (getPass(x0 - 1, y0 - 1) != 0))
+ // Want to go up and left
+ if (relDir == kRelDirLeftUp) {
+ if (getPass(x0 - 1, y0 - 1) != 0)
+ // Can go north-west
return kDirNW;
- if (((y0 - 1) >= 0) && (getPass(x0, y0 - 1) != 0))
+ if (getPass(x0 , y0 - 1) != 0)
+ // Can only go north
return kDirN;
- if (((x0 - 1) >= 0) && (getPass(x0 - 1, y0) != 0))
+ if (getPass(x0 - 1, y0 ) != 0)
+ // Can only go west
return kDirW;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kLeft | kDown)) {
- if (((x0 - 1) >= 0) && ((y0 + 1) < _mapHeight) &&
- (getPass(x0 - 1, y0 + 1) != 0))
+ // Want to go left and down
+ if (relDir == kRelDirLeftDown) {
+ if (getPass(x0 - 1, y0 + 1) != 0)
+ // Can go south-west
return kDirSW;
- if (((y0 + 1) < _mapHeight) && (getPass(x0, y0 + 1) != 0))
+ if (getPass(x0 , y0 + 1) != 0)
+ // Can only go south
return kDirS;
- if (((x0 - 1) >= 0) && (getPass(x0 - 1, y0) != 0))
+ if (getPass(x0 - 1, y0 ) != 0)
+ // Can only go west
return kDirW;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- return -1;
+
+ warning("Map::getDirection(): Invalid direction?!?");
+ return kDirNone;
}
int16 Map::findNearestWayPoint(int16 x, int16 y) {
@@ -236,7 +349,7 @@ int16 Map::findNearestWayPoint(int16 x, int16 y) {
length = 30000;
- for (int i = 0; i < _wayPointsCount; i++) {
+ for (int i = 0; i < _wayPointCount; i++) {
if ((_wayPoints[i].x < 0) || (_wayPoints[i].x >= _mapWidth) ||
(_wayPoints[i].y < 0) || (_wayPoints[i].y >= _mapHeight))
break;
@@ -318,73 +431,81 @@ void Map::findNearestWalkable(int16 &gobDestX, int16 &gobDestY,
gobDestY -= distance;
}
-int16 Map::checkDirectPath(Mult::Mult_Object *obj,
- int16 x0, int16 y0, int16 x1, int16 y1) {
- uint16 dir;
+void Map::moveDirection(Direction dir, int16 &x, int16 &y) {
+ switch (dir) {
+ case kDirNW:
+ x--;
+ y--;
+ break;
+
+ case kDirN:
+ y--;
+ break;
+
+ case kDirNE:
+ x++;
+ y--;
+ break;
+
+ case kDirW:
+ x--;
+ break;
+
+ case kDirE:
+ x++;
+ break;
+
+ case kDirSW:
+ x--;
+ y++;
+ break;
+
+ case kDirS:
+ y++;
+ break;
+
+ case kDirSE:
+ x++;
+ y++;
+ break;
+
+ default:
+ break;
+ }
+}
+
+int16 Map::checkDirectPath(Mult::Mult_Object *obj, int16 x0, int16 y0, int16 x1, int16 y1) {
while (1) {
- dir = getDirection(x0, y0, x1, y1);
+ Direction dir = getDirection(x0, y0, x1, y1);
if (obj) {
- if (obj->nearestWayPoint < obj->nearestDest) {
- if (_wayPoints[obj->nearestWayPoint + 1].notWalkable == 1)
- return 3;
- } else if (obj->nearestWayPoint > obj->nearestDest) {
- if (obj->nearestDest > 0)
- if (_wayPoints[obj->nearestDest - 1].notWalkable == 1)
+ // Check for a blocking waypoint
+
+ if (obj->nearestWayPoint < obj->nearestDest)
+ if ((obj->nearestWayPoint + 1) < _wayPointCount)
+ if (_wayPoints[obj->nearestWayPoint + 1].notWalkable == 1)
+ return 3;
+
+ if (obj->nearestWayPoint > obj->nearestDest)
+ if (obj->nearestWayPoint > 0)
+ if (_wayPoints[obj->nearestWayPoint - 1].notWalkable == 1)
return 3;
- }
}
if ((x0 == x1) && (y0 == y1))
+ // Successfully reached the destination
return 1;
- if (dir == 0)
+ if (dir == kDirNone)
+ // No way
return 3;
- switch (dir) {
- case kDirNW:
- x0--;
- y0--;
- break;
-
- case kDirN:
- y0--;
- break;
-
- case kDirNE:
- x0++;
- y0--;
- break;
-
- case kDirW:
- x0--;
- break;
-
- case kDirE:
- x0++;
- break;
-
- case kDirSW:
- x0--;
- y0++;
- break;
-
- case kDirS:
- y0++;
- break;
-
- case kDirSE:
- x0++;
- y0++;
- break;
- }
+ moveDirection(dir, x0, y0);
}
}
-int16 Map::checkLongPath(int16 x0, int16 y0,
- int16 x1, int16 y1, int16 i0, int16 i1) {
- uint16 dir = 0;
+int16 Map::checkLongPath(int16 x0, int16 y0, int16 x1, int16 y1, int16 i0, int16 i1) {
int16 curX = x0;
int16 curY = y0;
int16 nextLink = 1;
@@ -417,47 +538,13 @@ int16 Map::checkLongPath(int16 x0, int16 y0,
return 1;
return 0;
}
- dir = getDirection(x0, y0, curX, curY);
- switch (dir) {
- case 0:
- return 0;
-
- case kDirNW:
- x0--;
- y0--;
- break;
- case kDirN:
- y0--;
- break;
-
- case kDirNE:
- x0++;
- y0--;
- break;
-
- case kDirW:
- x0--;
- break;
-
- case kDirE:
- x0++;
- break;
-
- case kDirSW:
- x0--;
- y0++;
- break;
-
- case kDirS:
- y0++;
- break;
+ Direction dir = getDirection(x0, y0, curX, curY);
+ if (dir == kDirNone)
+ // No way
+ return 0;
- case kDirSE:
- x0++;
- y0++;
- break;
- }
+ moveDirection(dir, x0, y0);
}
}
diff --git a/engines/gob/map.h b/engines/gob/map.h
index 4a63e84a63..4bf2dc6228 100644
--- a/engines/gob/map.h
+++ b/engines/gob/map.h
@@ -30,54 +30,48 @@
namespace Gob {
+enum RelativeDirection {
+ kRelDirNone = 0 ,
+
+ kRelDirLeft = (1 << 0),
+ kRelDirUp = (1 << 1),
+ kRelDirRight = (1 << 2),
+ kRelDirDown = (1 << 3),
+
+ kRelDirLeftUp = kRelDirLeft | kRelDirUp,
+ kRelDirLeftDown = kRelDirLeft | kRelDirDown,
+ kRelDirRightUp = kRelDirRight | kRelDirUp,
+ kRelDirRightDown = kRelDirRight | kRelDirDown
+};
+
// The same numeric values are also used for the arrow keys.
+enum Direction {
+ kDirNone = 0x0000,
+ kDirNW = 0x4700,
+ kDirN = 0x4800,
+ kDirNE = 0x4900,
+ kDirW = 0x4B00,
+ kDirE = 0x4D00,
+ kDirSW = 0x4F00,
+ kDirS = 0x5000,
+ kDirSE = 0x5100
+};
+
+struct WayPoint {
+ int16 x;
+ int16 y;
+ int16 notWalkable;
+};
+
+struct ItemPos {
+ int8 x;
+ int8 y;
+ int8 orient;
+};
+
class Map {
public:
- enum {
- kDirNW = 0x4700,
- kDirN = 0x4800,
- kDirNE = 0x4900,
- kDirW = 0x4B00,
- kDirE = 0x4D00,
- kDirSW = 0x4F00,
- kDirS = 0x5000,
- kDirSE = 0x5100
- };
-
-#include "common/pack-start.h" // START STRUCT PACKING
-
- struct Point {
- int16 x;
- int16 y;
- int16 notWalkable;
- } PACKED_STRUCT;
-
-#define szMap_ItemPos 3
-
- struct ItemPos {
- int8 x;
- int8 y;
- int8 orient;
- } PACKED_STRUCT;
-
-#include "common/pack-end.h" // END STRUCT PACKING
-
- byte _widthByte;
- int16 _mapWidth;
- int16 _mapHeight;
- int16 _screenWidth;
- int16 _screenHeight;
- int16 _tilesWidth;
- int16 _tilesHeight;
- int16 _passWidth;
- bool _bigTiles;
- bool _mapUnknownBool;
-
- int8 *_passMap; // [y * _mapWidth + x], getPass(x, y);
- int16 **_itemsMap; // [y][x]
- int16 _wayPointsCount;
- Point *_wayPoints;
int16 _nearestWayPoint;
int16 _nearestDest;
@@ -89,12 +83,36 @@ public:
ItemPos _itemPoses[40];
char _sourceFile[15];
+ Map(GobEngine *vm);
+ virtual ~Map();
+
+ uint8 getVersion() const;
+
+ int16 getMapWidth() const;
+ int16 getMapHeight() const;
+
+ int16 getScreenWidth() const;
+ int16 getScreenHeight() const;
+
+ int16 getTilesWidth() const;
+ int16 getTilesHeight() const;
+
+ bool hasBigTiles() const;
+
+ int8 getPass(int x, int y, int width = -1) const;
+ void setPass(int x, int y, int8 pass, int width = -1);
+
+ const WayPoint &getWayPoint(int n) const;
+
void findNearestWalkable(int16 &gobDestX, int16 &gobDestY,
int16 mouseX, int16 mouseY);
+ int16 getItem(int x, int y) const;
+ void setItem(int x, int y, int16 item);
void placeItem(int16 x, int16 y, int16 id);
- int16 getDirection(int16 x0, int16 y0, int16 x1, int16 y1);
+ Direction getDirection(int16 x0, int16 y0, int16 x1, int16 y1);
+
int16 checkDirectPath(Mult::Mult_Object *obj, int16 x0,
int16 y0, int16 x1, int16 y1);
int16 checkLongPath(int16 x0, int16 y0,
@@ -102,65 +120,52 @@ public:
void loadMapsInitGobs();
- virtual int16 getItem(int x, int y) = 0;
- virtual void setItem(int x, int y, int16 item) = 0;
-
- virtual int8 getPass(int x, int y, int heightOff = -1) = 0;
- virtual void setPass(int x, int y, int8 pass, int heightOff = -1) = 0;
-
virtual void loadMapObjects(const char *avjFile) = 0;
virtual void findNearestToGob(Mult::Mult_Object *obj) = 0;
virtual void findNearestToDest(Mult::Mult_Object *obj) = 0;
virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y) = 0;
- Map(GobEngine *vm);
- virtual ~Map();
-
protected:
+ GobEngine *_vm;
+
bool _loadFromAvo;
- GobEngine *_vm;
+ uint8 _mapVersion;
- int16 findNearestWayPoint(int16 x, int16 y);
-};
+ int16 _mapWidth;
+ int16 _mapHeight;
-class Map_v1 : public Map {
-public:
- virtual void loadMapObjects(const char *avjFile);
- virtual void findNearestToGob(Mult::Mult_Object *obj);
- virtual void findNearestToDest(Mult::Mult_Object *obj);
- virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y);
+ int16 _screenWidth;
+ int16 _screenHeight;
- virtual int16 getItem(int x, int y) {
- assert(_itemsMap);
+ int16 _tilesWidth;
+ int16 _tilesHeight;
- x = CLIP<int>(x, 0, _mapWidth - 1);
- y = CLIP<int>(y, 0, _mapHeight - 1);
+ bool _bigTiles;
- return _itemsMap[y][x];
- }
- virtual void setItem(int x, int y, int16 item) {
- assert(_itemsMap);
+ bool _mapUnknownBool;
- x = CLIP<int>(x, 0, _mapWidth - 1);
- y = CLIP<int>(y, 0, _mapHeight - 1);
+ int16 _passWidth;
+ int8 *_passMap; // [y * _mapWidth + x], getPass(x, y);
- _itemsMap[y][x] = item;
- }
+ int16 _wayPointCount;
+ WayPoint *_wayPoints;
- virtual int8 getPass(int x, int y, int heightOff = -1) {
- if (!_passMap)
- return 0;
+ int16 **_itemsMap; // [y][x]
- return _passMap[y * _mapWidth + x];
- }
+ int16 findNearestWayPoint(int16 x, int16 y);
- virtual void setPass(int x, int y, int8 pass, int heightOff = -1) {
- if (!_passMap)
- return;
+private:
+ // Move the x and y values according to the direction
+ void moveDirection(Direction dir, int16 &x, int16 &y);
+};
- _passMap[y * _mapWidth + x] = pass;
- }
+class Map_v1 : public Map {
+public:
+ virtual void loadMapObjects(const char *avjFile);
+ virtual void findNearestToGob(Mult::Mult_Object *obj);
+ virtual void findNearestToDest(Mult::Mult_Object *obj);
+ virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y);
Map_v1(GobEngine *vm);
virtual ~Map_v1();
@@ -180,24 +185,6 @@ public:
virtual void findNearestToDest(Mult::Mult_Object *obj);
virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y);
- virtual int8 getPass(int x, int y, int heightOff = -1) {
- if (!_passMap)
- return 0;
-
- if (heightOff == -1)
- heightOff = _passWidth;
- return _passMap[y * heightOff + x];
- }
-
- virtual void setPass(int x, int y, int8 pass, int heightOff = -1) {
- if (!_passMap)
- return;
-
- if (heightOff == -1)
- heightOff = _passWidth;
- _passMap[y * heightOff + x] = pass;
- }
-
Map_v2(GobEngine *vm);
virtual ~Map_v2();
diff --git a/engines/gob/map_v1.cpp b/engines/gob/map_v1.cpp
index d8898c83d3..372f501887 100644
--- a/engines/gob/map_v1.cpp
+++ b/engines/gob/map_v1.cpp
@@ -23,7 +23,7 @@
*
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "gob/gob.h"
#include "gob/map.h"
@@ -44,7 +44,8 @@ void Map_v1::init() {
if (_passMap || _itemsMap)
return;
- _mapWidth = 26;
+ _passWidth = 26;
+ _mapWidth = 26;
_mapHeight = 28;
_passMap = new int8[_mapHeight * _mapWidth];
@@ -56,9 +57,9 @@ void Map_v1::init() {
memset(_itemsMap[i], 0, _mapWidth * sizeof(int16));
}
- _wayPointsCount = 40;
- _wayPoints = new Point[40];
- memset(_wayPoints, 0, sizeof(Point));
+ _wayPointCount = 40;
+ _wayPoints = new WayPoint[40];
+ memset(_wayPoints, 0, sizeof(WayPoint));
}
void Map_v1::loadMapObjects(const char *avjFile) {
@@ -74,13 +75,13 @@ void Map_v1::loadMapObjects(const char *avjFile) {
strcpy(avoName, _sourceFile);
strcat(avoName, ".avo");
- if (_vm->_dataIO->existData(avoName)) {
- _loadFromAvo = true;
- dataBuf = _vm->_dataIO->getData(avoName);
- } else {
+ int32 size;
+ dataBuf = _vm->_dataIO->getFile(avoName, size);
+ if (!dataBuf) {
+ dataBuf = _vm->_dataIO->getFile(avjFile, size);
_loadFromAvo = false;
- dataBuf = _vm->_dataIO->getData(avjFile);
- }
+ } else
+ _loadFromAvo = true;
Common::MemoryReadStream mapData(dataBuf, 4294967295U);
@@ -97,7 +98,12 @@ void Map_v1::loadMapObjects(const char *avjFile) {
_wayPoints[i].x = mapData.readUint16LE();
_wayPoints[i].y = mapData.readUint16LE();
}
- mapData.read(_itemPoses, szMap_ItemPos * 20);
+
+ for (int i = 0; i < 20; i++) {
+ _itemPoses[i].x = mapData.readByte();
+ _itemPoses[i].y = mapData.readByte();
+ _itemPoses[i].orient = mapData.readByte();
+ }
}
mapData.skip(32 + 76 + 4 + 20);
@@ -159,7 +165,7 @@ void Map_v1::loadSounds(Common::SeekableReadStream &data) {
_vm->_sound->sampleLoad(&_vm->_goblin->_soundData[14], SOUND_SND, "diamant1.snd");
for (int i = 0; i < count; i++) {
- if (!_vm->_dataIO->existData(sndNames[i]))
+ if (!_vm->_dataIO->hasFile(sndNames[i]))
continue;
_vm->_sound->sampleLoad(&_vm->_goblin->_soundData[i], SOUND_SND, sndNames[i]);
diff --git a/engines/gob/map_v2.cpp b/engines/gob/map_v2.cpp
index e2f9207003..f81d3e6002 100644
--- a/engines/gob/map_v2.cpp
+++ b/engines/gob/map_v2.cpp
@@ -100,11 +100,11 @@ void Map_v2::loadMapObjects(const char *avjFile) {
Common::SeekableReadStream &mapData = *resource->stream();
- _widthByte = mapData.readByte();
- if (_widthByte == 4) {
+ _mapVersion = mapData.readByte();
+ if (_mapVersion == 4) {
_screenWidth = 640;
_screenHeight = 400;
- } else if (_widthByte == 3) {
+ } else if (_mapVersion == 3) {
_passWidth = 65;
_screenWidth = 640;
_screenHeight = 200;
@@ -114,14 +114,14 @@ void Map_v2::loadMapObjects(const char *avjFile) {
_screenHeight = 200;
}
- _wayPointsCount = mapData.readByte();
+ _wayPointCount = mapData.readByte();
_tilesWidth = mapData.readSint16LE();
_tilesHeight = mapData.readSint16LE();
_bigTiles = !(_tilesHeight & 0xFF00);
_tilesHeight &= 0xFF;
- if (_widthByte == 4) {
+ if (_mapVersion == 4) {
_screenWidth = mapData.readSint16LE();
_screenHeight = mapData.readSint16LE();
}
@@ -133,19 +133,19 @@ void Map_v2::loadMapObjects(const char *avjFile) {
mapData.skip(_mapWidth * _mapHeight);
if (resource->getData()[0] == 1)
- wayPointsCount = _wayPointsCount = 40;
+ wayPointsCount = _wayPointCount = 40;
else
- wayPointsCount = _wayPointsCount == 0 ? 1 : _wayPointsCount;
+ wayPointsCount = _wayPointCount == 0 ? 1 : _wayPointCount;
delete[] _wayPoints;
- _wayPoints = new Point[wayPointsCount];
- for (int i = 0; i < _wayPointsCount; i++) {
+ _wayPoints = new WayPoint[wayPointsCount];
+ for (int i = 0; i < _wayPointCount; i++) {
_wayPoints[i].x = mapData.readSByte();
_wayPoints[i].y = mapData.readSByte();
_wayPoints[i].notWalkable = mapData.readSByte();
}
- if (_widthByte == 4) {
+ if (_mapVersion == 4) {
_mapWidth = VAR(17);
_passWidth = _mapWidth;
}
@@ -258,17 +258,24 @@ void Map_v2::findNearestToDest(Mult::Mult_Object *obj) {
void Map_v2::optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y) {
if (obj->nearestWayPoint < obj->nearestDest) {
+
for (int i = obj->nearestWayPoint; i <= obj->nearestDest; i++) {
if (checkDirectPath(obj, x, y, _wayPoints[i].x, _wayPoints[i].y) == 1)
obj->nearestWayPoint = i;
}
+
} else {
- for (int i = obj->nearestWayPoint;
- i >= obj->nearestDest && (_wayPoints[i].notWalkable != 1); i--) {
+
+ for (int i = obj->nearestWayPoint; i >= obj->nearestDest; i--) {
+ if (_wayPoints[i].notWalkable == 1)
+ break;
+
if (checkDirectPath(obj, x, y, _wayPoints[i].x, _wayPoints[i].y) == 1)
obj->nearestWayPoint = i;
}
+
}
+
}
} // End of namespace Gob
diff --git a/engines/gob/module.mk b/engines/gob/module.mk
index 05658e0ca8..882a67bd45 100644
--- a/engines/gob/module.mk
+++ b/engines/gob/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/gob
MODULE_OBJS := \
+ console.o \
dataio.o \
detection.o \
draw.o \
diff --git a/engines/gob/resources.cpp b/engines/gob/resources.cpp
index 8241821039..56941aa4c1 100644
--- a/engines/gob/resources.cpp
+++ b/engines/gob/resources.cpp
@@ -25,7 +25,7 @@
#include "common/util.h"
#include "common/endian.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "gob/gob.h"
#include "gob/resources.h"
@@ -71,7 +71,7 @@ int16 Resource::getHeight() const {
return _height;
}
-Common::MemoryReadStream *Resource::stream() const {
+Common::SeekableReadStream *Resource::stream() const {
return _stream;
}
@@ -95,7 +95,7 @@ int32 TextItem::getSize() const {
return _size;
}
-Common::MemoryReadStream *TextItem::stream() const {
+Common::SeekableReadStream *TextItem::stream() const {
return _stream;
}
@@ -293,7 +293,7 @@ bool Resources::loadTOTResourceTable() {
bool Resources::loadEXTResourceTable() {
_extResourceTable = new EXTResourceTable;
- DataStream *stream = _vm->_dataIO->getDataStream(_extFile.c_str());
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(_extFile);
if (!stream)
return false;
@@ -396,7 +396,7 @@ bool Resources::loadIMFile() {
imFile += num;
- DataStream *stream = _vm->_dataIO->getDataStream(imFile.c_str());
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(imFile);
if (!stream)
return true;
@@ -431,7 +431,7 @@ bool Resources::loadEXFile() {
_exFile = "commun.ex";
_exFile += totProps.exFileNumber + '0';
- if (!_vm->_dataIO->existData(_exFile.c_str())) {
+ if (!_vm->_dataIO->hasFile(_exFile)) {
_exFile.clear();
return true;
}
@@ -473,7 +473,7 @@ Common::String Resources::getLocTextFile(const Common::String &fileBase,
break;
}
- if (!_vm->_dataIO->existData(locTextFile.c_str()))
+ if (!_vm->_dataIO->hasFile(locTextFile))
locTextFile.clear();
return locTextFile;
@@ -525,8 +525,7 @@ byte *Resources::loadTOTLocTexts(const Common::String &fileBase, int32 &size) {
if (locTextFile.empty())
return 0;
- size = _vm->_dataIO->getDataSize(locTextFile.c_str());
- return _vm->_dataIO->getData(locTextFile.c_str());
+ return _vm->_dataIO->getFile(locTextFile, size);
}
Resource *Resources::getResource(uint16 id, int16 *width, int16 *height) const {
@@ -682,10 +681,10 @@ Resource *Resources::getEXTResource(uint16 id) const {
if (extItem.packed) {
byte *packedData = data;
- size = READ_LE_UINT32(packedData);
- data = new byte[size];
+ int32 unpackSize;
+ data = _vm->_dataIO->unpack(packedData, size, unpackSize);
- _vm->_dataIO->unpackData(packedData, data);
+ size = unpackSize;
delete[] packedData;
}
@@ -724,7 +723,7 @@ byte *Resources::getIMData(TOTResourceItem &totItem) const {
}
byte *Resources::getEXTData(EXTResourceItem &extItem, uint32 size) const {
- DataStream *stream = _vm->_dataIO->getDataStream(_extFile.c_str());
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(_extFile);
if (!stream)
return 0;
@@ -745,7 +744,7 @@ byte *Resources::getEXTData(EXTResourceItem &extItem, uint32 size) const {
}
byte *Resources::getEXData(EXTResourceItem &extItem, uint32 size) const {
- DataStream *stream = _vm->_dataIO->getDataStream(_exFile.c_str());
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(_exFile);
if (!stream)
return 0;
diff --git a/engines/gob/resources.h b/engines/gob/resources.h
index 7511185954..9921dc6e76 100644
--- a/engines/gob/resources.h
+++ b/engines/gob/resources.h
@@ -29,7 +29,7 @@
#include "common/str.h"
namespace Common {
- class MemoryReadStream;
+ class SeekableReadStream;
}
namespace Gob {
@@ -47,7 +47,7 @@ public:
int16 getWidth () const;
int16 getHeight() const;
- Common::MemoryReadStream *stream() const;
+ Common::SeekableReadStream *stream() const;
private:
byte *_data;
@@ -56,7 +56,7 @@ private:
int16 _height;
bool _needFree;
- Common::MemoryReadStream *_stream;
+ Common::SeekableReadStream *_stream;
};
class TextItem {
@@ -67,13 +67,13 @@ public:
byte *getData() const;
int32 getSize() const;
- Common::MemoryReadStream *stream() const;
+ Common::SeekableReadStream *stream() const;
private:
byte *_data;
int32 _size;
- Common::MemoryReadStream *_stream;
+ Common::SeekableReadStream *_stream;
};
class Resources {
diff --git a/engines/gob/save/saveconverter.cpp b/engines/gob/save/saveconverter.cpp
index 7bfb2a2da2..59d313138b 100644
--- a/engines/gob/save/saveconverter.cpp
+++ b/engines/gob/save/saveconverter.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/memstream.h"
#include "common/savefile.h"
#include "gob/gob.h"
diff --git a/engines/gob/save/saveconverter.h b/engines/gob/save/saveconverter.h
index 3ae48e1edc..dd5f43875e 100644
--- a/engines/gob/save/saveconverter.h
+++ b/engines/gob/save/saveconverter.h
@@ -77,7 +77,7 @@ protected:
Common::String _fileName;
byte *_data;
- Common::MemoryReadStream *_stream;
+ Common::SeekableReadStream *_stream;
Common::InSaveFile *openSave() const;
diff --git a/engines/gob/save/savefile.cpp b/engines/gob/save/savefile.cpp
index 79e931fe30..3869ea2def 100644
--- a/engines/gob/save/savefile.cpp
+++ b/engines/gob/save/savefile.cpp
@@ -25,6 +25,7 @@
#include "common/util.h"
#include "common/endian.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "common/savefile.h"
diff --git a/engines/gob/save/savefile.h b/engines/gob/save/savefile.h
index da3696dee8..980616bb74 100644
--- a/engines/gob/save/savefile.h
+++ b/engines/gob/save/savefile.h
@@ -35,13 +35,14 @@ namespace Gob {
class GobEngine;
class Surface;
-/** A class wrapping a save part header.
- *
- * A save part header consists of 4 fields:
- * ID : The 8 character ID \0SCVMGOB
- * Type : The 4 character ID for this part's type
- * Version : This part's version. Each type has its own version counter
- * Size : The size of the contents, i.e. excluding this header
+/**
+ * A class wrapping a save part header.
+ *
+ * A save part header consists of 4 fields:
+ * ID : The 8 character ID \0SCVMGOB
+ * Type : The 4 character ID for this part's type
+ * Version : This part's version. Each type has its own version counter
+ * Size : The size of the contents, i.e. excluding this header
*/
class SaveHeader {
public:
@@ -186,13 +187,13 @@ public:
static const uint32 kVersion = 1;
static const uint32 kID = MKID_BE('INFO');
- /** The constructor.
- *
- * @param descMaxLength The maximal number of bytes that fit into the description.
- * @param gameID An ID for the game (Gob1, Gob2, Gob3, ...).
- * @param gameVersion An ID for game specific versioning
- * @param endian Endianness of the platform the game originally ran on.
- * @param varCount The number of script variables.
+ /**
+ * The constructor.
+ * @param descMaxLength The maximal number of bytes that fit into the description.
+ * @param gameID An ID for the game (Gob1, Gob2, Gob3, ...).
+ * @param gameVersion An ID for game specific versioning
+ * @param endian Endianness of the platform the game originally ran on.
+ * @param varCount The number of script variables.
*/
SavePartInfo(uint32 descMaxLength, uint32 gameID,
uint32 gameVersion, byte endian, uint32 varCount);
@@ -228,10 +229,10 @@ public:
static const uint32 kVersion = 1;
static const uint32 kID = MKID_BE('CONT');
- /** The constructor.
- *
- * @param partCount The number parts this container shall hold.
- * @param slot The save slot this save's for.
+ /**
+ * The constructor.
+ * @param partCount The number parts this container shall hold.
+ * @param slot The save slot this save's for.
*/
SaveContainer(uint32 partCount, uint32 slot);
~SaveContainer();
diff --git a/engines/gob/script.cpp b/engines/gob/script.cpp
index 339199c9b1..0b0fcd4cda 100644
--- a/engines/gob/script.cpp
+++ b/engines/gob/script.cpp
@@ -43,7 +43,7 @@ Script::Script(GobEngine *vm) : _vm(vm) {
_totPtr = 0;
_totSize = 0;
- _lomHandle = -1;
+ _lom = 0;
memset(&_totProperties, 0, sizeof(TOTFile::Properties));
}
@@ -380,20 +380,16 @@ bool Script::loadTOT(const Common::String &fileName) {
bool Script::loadLOM(const Common::String &fileName) {
warning("Stub: Script::loadLOM(%s)", _totFile.c_str());
- _lomHandle = _vm->_dataIO->openData(_totFile.c_str());
- if (_lomHandle < 0)
+ _lom = _vm->_dataIO->getFile(_totFile);
+ if (!_lom)
return false;
- DataStream *stream = _vm->_dataIO->openAsStream(_lomHandle);
-
- stream->seek(48);
- _totSize = stream->readUint32LE();
- stream->seek(0);
+ _lom->seek(48);
+ _totSize = _lom->readUint32LE();
+ _lom->seek(0);
_totData = new byte[_totSize];
- stream->read(_totData, _totSize);
-
- delete stream;
+ _lom->read(_totData, _totSize);
return false;
}
@@ -403,8 +399,8 @@ void Script::unload() {
}
void Script::unloadTOT() {
- if (_lomHandle >= 0)
- _vm->_dataIO->closeData(_lomHandle);
+ delete _lom;
+ _lom = 0;
// Unwind the call stack
while (!_callStack.empty())
@@ -415,7 +411,6 @@ void Script::unloadTOT() {
_totData = 0;
_totSize = 0;
_totPtr = 0;
- _lomHandle = -1;
_totFile.clear();
_finished = true;
@@ -518,7 +513,7 @@ uint16 Script::getFunctionOffset(uint8 function) const {
}
uint32 Script::getVariablesCount(const char *fileName, GobEngine *vm) {
- DataStream *stream = vm->_dataIO->getDataStream(fileName);
+ Common::SeekableReadStream *stream = vm->_dataIO->getFile(fileName);
if (!stream)
return 0;
diff --git a/engines/gob/script.h b/engines/gob/script.h
index 84daeaf1af..cf9eb246ce 100644
--- a/engines/gob/script.h
+++ b/engines/gob/script.h
@@ -150,7 +150,7 @@ private:
byte *_totPtr;
uint32 _totSize;
- int16 _lomHandle;
+ Common::SeekableReadStream *_lom;
TOTFile::Properties _totProperties;
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 28123668cc..d643ae511b 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -240,7 +240,7 @@ void AdLib::setKey(byte voice, byte note, bool on, bool spec) {
freq = _freqs[_notLin[voice]][note - octa * 12];
writeOPL(0xA0 + voice, freq & 0xFF);
- writeOPL(0xB0 + voice, (freq >> 8) | (octa << 2) | 0x20 * on);
+ writeOPL(0xB0 + voice, (freq >> 8) | (octa << 2) | (0x20 * (on ? 1 : 0)));
if (!freq)
warning("AdLib::setKey Voice %d, note %02X unknown", voice, note);
diff --git a/engines/gob/sound/cdrom.cpp b/engines/gob/sound/cdrom.cpp
index ec7da29fdb..4f3d783046 100644
--- a/engines/gob/sound/cdrom.cpp
+++ b/engines/gob/sound/cdrom.cpp
@@ -24,11 +24,13 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/util.h"
+#include "backends/audiocd/audiocd.h"
+
#include "gob/gob.h"
#include "gob/sound/cdrom.h"
-#include "gob/helper.h"
#include "gob/dataio.h"
namespace Gob {
@@ -48,7 +50,7 @@ CDROM::~CDROM() {
stop();
}
-void CDROM::readLIC(DataStream &stream) {
+void CDROM::readLIC(Common::SeekableReadStream &stream) {
uint16 version, startChunk, pos;
freeLICBuffer();
@@ -91,7 +93,7 @@ void CDROM::startTrack(const char *trackName) {
return;
}
- strncpy0(_curTrack, trackName, 15);
+ Common::strlcpy(_curTrack, trackName, 16);
stopPlaying();
_curTrackBuffer = matchPtr;
diff --git a/engines/gob/sound/cdrom.h b/engines/gob/sound/cdrom.h
index 6f01e6f90a..894744ca15 100644
--- a/engines/gob/sound/cdrom.h
+++ b/engines/gob/sound/cdrom.h
@@ -28,14 +28,12 @@
namespace Gob {
-class DataStream;
-
class CDROM {
public:
CDROM();
~CDROM();
- void readLIC(DataStream &stream);
+ void readLIC(Common::SeekableReadStream &stream);
void freeLICBuffer();
void startTrack(const char *trackName);
diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp
index bc4495fafd..dc80699ce0 100644
--- a/engines/gob/sound/sound.cpp
+++ b/engines/gob/sound/sound.cpp
@@ -114,19 +114,13 @@ bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName,
debugC(2, kDebugSound, "Loading sample \"%s\"", fileName);
- if (!_vm->_dataIO->existData(fileName)) {
+ int32 size;
+ byte *data = _vm->_dataIO->getFile(fileName, size);
+ if (!data) {
warning("Can't open sample file \"%s\"", fileName);
return false;
}
- byte *data;
- uint32 size;
-
- data = (byte *)_vm->_dataIO->getData(fileName);
- if (!data)
- return false;
-
- size = _vm->_dataIO->getDataSize(fileName);
return sndDesc->load(type, data, size);
}
@@ -279,13 +273,12 @@ bool Sound::adlibLoadMDY(const char *fileName) {
debugC(1, kDebugSound, "AdLib: Loading MDY data (\"%s\")", fileName);
- if (!_vm->_dataIO->existData(fileName)) {
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(fileName);
+ if (!stream) {
warning("Can't open MDY file \"%s\"", fileName);
return false;
}
- DataStream *stream = _vm->_dataIO->getDataStream(fileName);
-
bool loaded = _mdyPlayer->loadMDY(*stream);
delete stream;
@@ -300,15 +293,14 @@ bool Sound::adlibLoadTBR(const char *fileName) {
if (!_mdyPlayer)
_mdyPlayer = new MDYPlayer(*_vm->_mixer);
- if (!_vm->_dataIO->existData(fileName)) {
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(fileName);
+ if (!stream) {
warning("Can't open TBR file \"%s\"", fileName);
return false;
}
debugC(1, kDebugSound, "AdLib: Loading MDY instruments (\"%s\")", fileName);
- DataStream *stream = _vm->_dataIO->getDataStream(fileName);
-
bool loaded = _mdyPlayer->loadTBR(*stream);
delete stream;
@@ -522,13 +514,10 @@ void Sound::cdLoadLIC(const char *fname) {
debugC(1, kDebugSound, "CDROM: Loading LIC \"%s\"", fname);
- if (!_vm->_dataIO->existData(fname))
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(fname);
+ if (!stream)
return;
- _vm->_dataIO->getUnpackedData(fname);
-
- DataStream *stream = _vm->_dataIO->getDataStream(fname);
-
_cdrom->readLIC(*stream);
delete stream;
diff --git a/engines/gob/sound/sounddesc.cpp b/engines/gob/sound/sounddesc.cpp
index 2efb3e90f2..9df8bbb4bc 100644
--- a/engines/gob/sound/sounddesc.cpp
+++ b/engines/gob/sound/sounddesc.cpp
@@ -24,7 +24,7 @@
*/
#include "common/util.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
#include "sound/decoders/wave.h"
diff --git a/engines/gob/totfile.cpp b/engines/gob/totfile.cpp
index 178deeaf58..82dd0c38c0 100644
--- a/engines/gob/totfile.cpp
+++ b/engines/gob/totfile.cpp
@@ -45,11 +45,11 @@ TOTFile::~TOTFile() {
bool TOTFile::load(const Common::String &fileName) {
// Trying to open normally
- _stream = _vm->_dataIO->getDataStream(fileName.c_str());
+ _stream = _vm->_dataIO->getFile(fileName);
if (!_stream)
// Trying to open from video
- _stream = _vm->_vidPlayer->getEmbeddedFile(fileName.c_str());
+ _stream = _vm->_vidPlayer->getEmbeddedFile(fileName);
if (!_stream)
return false;
diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp
index 00d8c2c9ac..8e2b3d89cd 100644
--- a/engines/gob/util.cpp
+++ b/engines/gob/util.cpp
@@ -121,6 +121,10 @@ void Util::processInput(bool scroll) {
_fastMode ^= 2;
else if (event.kbd.keycode == Common::KEYCODE_p)
_vm->pauseGame();
+ else if (event.kbd.keycode == Common::KEYCODE_d) {
+ _vm->getDebugger()->attach();
+ _vm->getDebugger()->onFrame();
+ }
break;
}
addKeyToBuffer(event.kbd);
@@ -146,7 +150,7 @@ void Util::processInput(bool scroll) {
// WORKAROUND:
// Force a check of the mouse in order to fix the sofa bug. This apply only for Gob3, and only
// in the impacted TOT file so that the second screen animation is not broken.
- if ((_vm->getGameType() == kGameTypeGob3) && !strncmp(_vm->_game->_curTotFile, "EMAP1008.TOT", 12))
+ if ((_vm->getGameType() == kGameTypeGob3) && !scumm_stricmp(_vm->_game->_curTotFile, "EMAP1008.TOT"))
_vm->_game->evaluateScroll();
}
}
diff --git a/engines/gob/variables.cpp b/engines/gob/variables.cpp
index 11c5f08bb2..4f6bad52f0 100644
--- a/engines/gob/variables.cpp
+++ b/engines/gob/variables.cpp
@@ -24,10 +24,10 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/variables.h"
-#include "gob/helper.h"
namespace Gob {
@@ -112,7 +112,7 @@ uint32 Variables::readOff32(uint32 offset) const {
}
void Variables::readOffString(uint32 offset, char *value, uint32 length) {
- strncpy0(value, (const char *)(_vars + offset), length - 1);
+ Common::strlcpy(value, (const char *)(_vars + offset), length);
}
const uint8 *Variables::getAddressVar8(uint32 var) const {
diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp
index ee73f14dfa..91e3737832 100644
--- a/engines/gob/video.cpp
+++ b/engines/gob/video.cpp
@@ -331,9 +331,9 @@ void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height,
}
void Video::drawPackedSprite(const char *path, Surface &dest, int width) {
- byte *data;
+ int32 size;
+ byte *data = _vm->_dataIO->getFile(path, size);
- data = _vm->_dataIO->getData(path);
drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest);
delete[] data;
}
diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index f349413b93..3d29c2ce26 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -25,7 +25,6 @@
#include "gob/videoplayer.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/video.h"
@@ -501,7 +500,7 @@ bool VideoPlayer::hasEmbeddedFile(const Common::String &fileName, int slot) cons
return video->decoder->hasEmbeddedFile(fileName);
}
-Common::MemoryReadStream *VideoPlayer::getEmbeddedFile(const Common::String &fileName, int slot) {
+Common::SeekableReadStream *VideoPlayer::getEmbeddedFile(const Common::String &fileName, int slot) {
const Video *video = getVideoBySlot(slot);
if (!video)
return 0;
@@ -687,7 +686,7 @@ Common::String VideoPlayer::findFile(const Common::String &file, Properties &pro
if ((properties.type == kVideoTypeTry) || (properties.type == ((Type) i))) {
fileName = base + "." + _extensions[i];
- if (_vm->_dataIO->existData(fileName.c_str())) {
+ if (_vm->_dataIO->hasFile(fileName)) {
properties.type = (Type) i;
break;
}
@@ -708,7 +707,7 @@ Graphics::CoktelDecoder *VideoPlayer::openVideo(const Common::String &file, Prop
if (fileName.empty())
return 0;
- Common::SeekableReadStream *stream = _vm->_dataIO->getDataStream(fileName.c_str());
+ Common::SeekableReadStream *stream = _vm->_dataIO->getFile(fileName);
if (!stream)
return 0;
diff --git a/engines/gob/videoplayer.h b/engines/gob/videoplayer.h
index c154254455..45b3a7b82d 100644
--- a/engines/gob/videoplayer.h
+++ b/engines/gob/videoplayer.h
@@ -121,7 +121,7 @@ public:
const Common::List<Common::Rect> *getDirtyRects(int slot = 0) const;
bool hasEmbeddedFile(const Common::String &fileName, int slot = 0) const;
- Common::MemoryReadStream *getEmbeddedFile(const Common::String &fileName, int slot = 0);
+ Common::SeekableReadStream *getEmbeddedFile(const Common::String &fileName, int slot = 0);
int32 getSubtitleIndex(int slot = 0) const;
diff --git a/engines/groovie/font.cpp b/engines/groovie/font.cpp
index dc1d7ae73a..b9d6dbf687 100644
--- a/engines/groovie/font.cpp
+++ b/engines/groovie/font.cpp
@@ -62,6 +62,10 @@ bool T7GFont::load(Common::SeekableReadStream &stream) {
delete[] _glyphs;
_glyphs = new Glyph[numGlyphs];
+ // Ensure we're ready to read the first glyph. (Most versions don't
+ // need it, but the russian one does. This fixes bug #3095031.)
+ stream.seek(glyphOffsets[0]);
+
// Read the glyphs
_maxHeight = _maxWidth = 0;
for (int i = 0; (i < numGlyphs) && !stream.eos(); i++) {
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index c7c3ef85f8..f280da8f56 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -37,6 +37,7 @@
#include "common/events.h"
#include "common/macresman.h"
+#include "backends/audiocd/audiocd.h"
#include "engines/util.h"
#include "graphics/fontman.h"
#include "sound/mixer.h"
diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h
index 8ae5f4157f..f8fad8d91f 100644
--- a/engines/groovie/groovie.h
+++ b/engines/groovie/groovie.h
@@ -44,7 +44,7 @@ namespace Common {
* now fully completable. All remaining Groovie games use V2 of the engine,
* which is under slow development.
*
- * Supported games:
+ * Games using this engine:
* - The 7th Guest (completable)
* - The 11th Hour
* - Clandestiny
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index 2ad11ee0e6..fd2cc2bec8 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -27,8 +27,10 @@
#include "groovie/groovie.h"
#include "groovie/resource.h"
+#include "backends/audiocd/audiocd.h"
#include "common/config-manager.h"
#include "common/macresman.h"
+#include "common/memstream.h"
#include "sound/midiparser.h"
namespace Groovie {
diff --git a/engines/groovie/resource.cpp b/engines/groovie/resource.cpp
index 32cc1735ef..5d4ccf7d91 100644
--- a/engines/groovie/resource.cpp
+++ b/engines/groovie/resource.cpp
@@ -25,6 +25,7 @@
#include "common/archive.h"
#include "common/macresman.h"
+#include "common/substream.h"
#include "groovie/resource.h"
#include "groovie/groovie.h"
diff --git a/engines/groovie/saveload.cpp b/engines/groovie/saveload.cpp
index 423829c5fe..4e3e4cfcf8 100644
--- a/engines/groovie/saveload.cpp
+++ b/engines/groovie/saveload.cpp
@@ -26,6 +26,7 @@
#include "groovie/saveload.h"
#include "common/system.h"
+#include "common/substream.h"
#define SUPPORTED_SAVEFILE_VERSION 1
// 0 - Just script variables, compatible with the original
diff --git a/engines/groovie/saveload.h b/engines/groovie/saveload.h
index b2b81cb6db..24a0ddfbf8 100644
--- a/engines/groovie/saveload.h
+++ b/engines/groovie/saveload.h
@@ -28,6 +28,7 @@
#include "common/savefile.h"
#include "engines/game.h"
+#include "engines/savestate.h"
namespace Groovie {
diff --git a/engines/groovie/script.cpp b/engines/groovie/script.cpp
index 9fd7fa7d63..4abfff74f2 100644
--- a/engines/groovie/script.cpp
+++ b/engines/groovie/script.cpp
@@ -214,7 +214,7 @@ void Script::directGameLoad(int slot) {
void Script::step() {
// Prepare the base debug string
- _debugString = _scriptFile + Common::String::printf("@0x%04X: ", _currentInstruction);
+ _debugString = _scriptFile + Common::String::format("@0x%04X: ", _currentInstruction);
// Get the current opcode
byte opcode = readScript8bits();
@@ -222,7 +222,7 @@ void Script::step() {
opcode = opcode & 0x7F;
// Show the opcode debug string
- _debugString += Common::String::printf("op 0x%02X: ", opcode);
+ _debugString += Common::String::format("op 0x%02X: ", opcode);
// Only output if we're not re-doing the previous instruction
if (_currentInstruction != _oldInstruction) {
diff --git a/engines/hugo/engine.h b/engines/hugo/console.cpp
index 0d14d244b5..4c4f5934f8 100644
--- a/engines/hugo/engine.h
+++ b/engines/hugo/console.cpp
@@ -23,22 +23,21 @@
*
*/
-/*
- * This code is based on original Hugo 1-3 Trilogy source code
- *
- * Copyright (c) 1989-1995 David P. Gray
- *
- */
+#include "hugo/console.h"
+#include "hugo/hugo.h"
-#ifndef HUGO_ENGINE_H
-#define HUGO_ENGINE_H
namespace Hugo {
-enum seqTextEngine {
- // Strings used by the engine
- kEsAdvertise = 0
-};
+HugoConsole::HugoConsole(HugoEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
-} // End of namespace Hugo
+HugoConsole::~HugoConsole() {
+}
-#endif // HUGO_ENGINE_H
+void HugoConsole::preEnter() {
+}
+
+void HugoConsole::postEnter() {
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/console.h b/engines/hugo/console.h
new file mode 100644
index 0000000000..f6c651459e
--- /dev/null
+++ b/engines/hugo/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef HUGO_CONSOLE_H
+#define HUGO_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Hugo {
+
+class HugoEngine;
+
+class HugoConsole : public GUI::Debugger {
+public:
+ HugoConsole(HugoEngine *vm);
+ virtual ~HugoConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ HugoEngine *_vm;
+};
+
+} // End of namespace Hugo
+
+#endif
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
index 0064a39434..a4bc5a4c1c 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -41,8 +41,8 @@ uint32 HugoEngine::getFeatures() const {
static const PlainGameDescriptor hugoGames[] = {
// Games
{"hugo1", "Hugo 1: Hugo's House of Horrors"},
- {"hugo2", "Hugo 2: Hugo's Mystery Adventure"},
- {"hugo3", "Hugo 3: Hugo's Amazon Adventure"},
+ {"hugo2", "Hugo 2: Whodunit?"},
+ {"hugo3", "Hugo 3: Jungle of Doom"},
{0, 0}
};
@@ -194,8 +194,7 @@ void HugoEngine::initGame(const HugoGameDescription *gd) {
_packedFl = (getFeatures() & GF_PACKED);
_gameVariant = _gameType - 1 + ((_platform == Common::kPlatformWindows) ? 0 : 3);
- // Generate filenames
- _initFilename = _targetName + "-00.SAV";
+ // Generate filename
_saveFilename = _targetName + "-%d.SAV";
}
diff --git a/engines/hugo/display.cpp b/engines/hugo/display.cpp
index 3a8d0d4e89..f7975df693 100644
--- a/engines/hugo/display.cpp
+++ b/engines/hugo/display.cpp
@@ -40,7 +40,6 @@
namespace Hugo {
-#define NUM_COLORS 16 // Num colors to save in palette
#define DMAX 16 // Size of add/restore rect lists
#define BMAX (DMAX * 2) // Size of dirty rect blit list
@@ -48,8 +47,11 @@ namespace Hugo {
#define INY(Y, B) (Y >= B->y && Y <= B->y + B->dy)
#define OVERLAP(A, B) ((INX(A->x, B) || INX(A->x + A->dx, B) || INX(B->x, A) || INX(B->x + B->dx, A)) && (INY(A->y, B) || INY(A->y + A->dy, B) || INY(B->y, A) || INY(B->y + B->dy, A)))
-Screen::Screen(HugoEngine &vm) : _vm(vm) {
-
+Screen::Screen(HugoEngine *vm) : _vm(vm), _palette(0) {
+ for (int j = 0; j < NUM_FONTS; j++) {
+ _arrayFont[j] = 0;
+ fontLoadedFl[j] = false;
+ }
}
Screen::~Screen() {
@@ -58,16 +60,20 @@ Screen::~Screen() {
void Screen::createPal() {
debugC(1, kDebugDisplay, "createPal");
- g_system->setPalette(_vm._palette, 0, NUM_COLORS);
+ g_system->setPalette(_palette, 0, NUM_COLORS);
}
+/**
+* Create logical palette
+*/
void Screen::initDisplay() {
debugC(1, kDebugDisplay, "initDisplay");
- // Create logical palette
createPal();
}
-// Move an image from source to destination
+/**
+* Move an image from source to destination
+*/
void Screen::moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2) {
debugC(3, kDebugDisplay, "moveImage(srcImage, %d, %d, %d, %d, %d, dstImage, %d, %d, %d)", x1, y1, dx, dy, width1, x2, y2, width2);
@@ -91,51 +97,65 @@ void Screen::displayBackground() {
g_system->copyRectToScreen(_frontBuffer, 320, 0, 0, 320, 200);
}
-// Blit the supplied rectangle from _frontBuffer to the screen
+/**
+* Blit the supplied rectangle from _frontBuffer to the screen
+*/
void Screen::displayRect(int16 x, int16 y, int16 dx, int16 dy) {
debugC(3, kDebugDisplay, "displayRect(%d, %d, %d, %d)", x, y, dx, dy);
g_system->copyRectToScreen(&_frontBuffer[x + y * 320], 320, x, y, dx, dy);
}
+/**
+* Change a color by remapping supplied palette index with new index
+*/
void Screen::remapPal(uint16 oldIndex, uint16 newIndex) {
-// Change a color by remapping supplied palette index with new index
debugC(1, kDebugDisplay, "Remap_pal(%d, %d)", oldIndex, newIndex);
- warning("STUB: Remap_pal()");
- //bminfo.bmiColors[oldIndex] = ctab[newIndex];
+ byte pal[4];
+
+ pal[0] = _palette[newIndex * 4 + 0];
+ pal[1] = _palette[newIndex * 4 + 1];
+ pal[2] = _palette[newIndex * 4 + 2];
+ pal[3] = _palette[newIndex * 4 + 3];
+
+ g_system->setPalette(pal, oldIndex, 1);
}
void Screen::savePal(Common::WriteStream *f) {
debugC(1, kDebugDisplay, "savePal");
- warning("STUB: savePal()");
- //fwrite(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f);
+ for (int i = 0; i < _paletteSize; i++)
+ f->writeByte(_palette[i]);
}
void Screen::restorePal(Common::SeekableReadStream *f) {
debugC(1, kDebugDisplay, "restorePal");
- warning("STUB: restorePal()");
- //fread(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f);
+ for (int i = 0; i < _paletteSize; i++)
+ _palette[i] = f->readByte();
}
-// Set the new background color
+/**
+* Set the new background color
+*/
void Screen::setBackgroundColor(long color) {
debugC(1, kDebugDisplay, "setBackgroundColor(%ld)", color);
// How??? Translate existing pixels in dib before objects rendered?
}
-// Return the overlay state (Foreground/Background) of the currently
-// processed object by looking down the current column for an overlay
-// base bit set (in which case the object is foreground).
+/**
+* Return the overlay state (Foreground/Background) of the currently
+* processed object by looking down the current column for an overlay
+* base bit set (in which case the object is foreground).
+*/
overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
debugC(4, kDebugDisplay, "findOvl");
for (; y < seq_p->lines; y++) { // Each line in object
- image_pt ovb_p = _vm.getBaseBoundaryOverlay() + ((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits
+ image_pt ovb_p = _vm->getBaseBoundaryOverlay() + ((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits
if (*ovb_p & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set
return FG; // Found a bit - must be foreground
dst_p += XPIX;
@@ -144,14 +164,16 @@ overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
return BG; // No bits set, must be background
}
-// Merge an object frame into _frontBuffer at sx, sy and update rectangle list.
-// If fore TRUE, force object above any overlay
+/**
+* Merge an object frame into _frontBuffer at sx, sy and update rectangle list.
+* If fore TRUE, force object above any overlay
+*/
void Screen::displayFrame(int sx, int sy, seq_t *seq, bool foreFl) {
debugC(3, kDebugDisplay, "displayFrame(%d, %d, seq, %d)", sx, sy, (foreFl) ? 1 : 0);
image_pt image = seq->imagePtr; // Ptr to object image data
image_pt subFrontBuffer = &_frontBuffer[sy * XPIX + sx]; // Ptr to offset in _frontBuffer
- image_pt overlay = &_vm.getFirstOverlay()[(sy * XPIX + sx) >> 3]; // Ptr to overlay data
+ image_pt overlay = &_vm->getFirstOverlay()[(sy * XPIX + sx) >> 3]; // Ptr to overlay data
int16 frontBufferwrap = XPIX - seq->x2 - 1; // Wraps dest_p after each line
int16 imageWrap = seq->bytesPerLine8 - seq->x2 - 1;
@@ -159,7 +181,7 @@ void Screen::displayFrame(int sx, int sy, seq_t *seq, bool foreFl) {
for (uint16 y = 0; y < seq->lines; y++) { // Each line in object
for (uint16 x = 0; x <= seq->x2; x++) {
if (*image) { // Non-transparent
- overlay = _vm.getFirstOverlay() + ((uint16)(subFrontBuffer - _frontBuffer) >> 3); // Ptr into overlay bits
+ overlay = _vm->getFirstOverlay() + ((uint16)(subFrontBuffer - _frontBuffer) >> 3); // Ptr into overlay bits
if (*overlay & (0x80 >> ((uint16)(subFrontBuffer - _frontBuffer) & 7))) { // Overlay bit is set
if (overlayState == UNDEF) // Overlay defined yet?
overlayState = findOvl(seq, subFrontBuffer, y);// No, find it.
@@ -180,7 +202,9 @@ void Screen::displayFrame(int sx, int sy, seq_t *seq, bool foreFl) {
displayList(D_ADD, sx, sy, seq->x2 + 1, seq->lines);
}
-// Merge rectangles A,B leaving result in B
+/**
+* Merge rectangles A,B leaving result in B
+*/
void Screen::merge(rect_t *rectA, rect_t *rectB) {
debugC(6, kDebugDisplay, "merge");
@@ -195,10 +219,12 @@ void Screen::merge(rect_t *rectA, rect_t *rectB) {
rectB->dy = MAX(ya, yb) - rectB->y;
}
-// Coalesce the rectangles in the restore/add list into one unified
-// blist. len is the sizes of alist or rlist. blen is current length
-// of blist. bmax is the max size of the blist. Note that blist can
-// have holes, in which case dx = 0. Returns used length of blist.
+/**
+* Coalesce the rectangles in the restore/add list into one unified
+* blist. len is the sizes of alist or rlist. blen is current length
+* of blist. bmax is the max size of the blist. Note that blist can
+* have holes, in which case dx = 0. Returns used length of blist.
+*/
int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax) {
debugC(4, kDebugDisplay, "mergeLists");
@@ -213,7 +239,7 @@ int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int
if (OVERLAP(list, bp))
coalesce[c++] = b;
}
-
+
// Any overlapping blit rects?
if (c == 0) { // None, add a new entry
blist[blen++] = *list;
@@ -233,8 +259,10 @@ int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int
return blen;
}
-// Process the display list
-// Trailing args are int16 x,y,dx,dy for the D_ADD operation
+/**
+* Process the display list
+* Trailing args are int16 x,y,dx,dy for the D_ADD operation
+*/
void Screen::displayList(dupdate_t update, ...) {
debugC(6, kDebugDisplay, "displayList");
@@ -253,7 +281,7 @@ void Screen::displayList(dupdate_t update, ...) {
break;
case D_ADD: // Add a rectangle to list
if (addIndex >= DMAX) {
- Utils::Warn("%s", "Display list exceeded");
+ warning("Display list exceeded");
return;
}
va_start(marker, update); // Initialize variable arguments
@@ -269,8 +297,8 @@ void Screen::displayList(dupdate_t update, ...) {
// Don't blit if newscreen just loaded because _frontBuffer will
// get blitted via InvalidateRect() at end of this cycle
// and blitting here causes objects to appear too soon.
- if (_vm.getGameStatus().newScreenFl) {
- _vm.getGameStatus().newScreenFl = false;
+ if (_vm->getGameStatus().newScreenFl) {
+ _vm->getGameStatus().newScreenFl = false;
break;
}
@@ -295,13 +323,14 @@ void Screen::displayList(dupdate_t update, ...) {
}
}
+/**
+* Write supplied character (font data) at sx,sy in supplied color
+* Font data as follows:
+* *(fontdata+1) = Font Height (pixels)
+* *(fontdata+1) = Font Width (pixels)
+* *(fontdata+x) = Font Bitmap (monochrome)
+*/
void Screen::writeChr(int sx, int sy, byte color, char *local_fontdata) {
-// Write supplied character (font data) at sx,sy in supplied color
-// Font data as follows:
-//
-// *(fontdata+1) = Font Height (pixels)
-// *(fontdata+1) = Font Width (pixels)
-// *(fontdata+x) = Font Bitmap (monochrome)
debugC(2, kDebugDisplay, "writeChr(%d, %d, %d, %d)", sx, sy, color, local_fontdata[0]);
byte height = local_fontdata[0];
@@ -320,7 +349,9 @@ void Screen::writeChr(int sx, int sy, byte color, char *local_fontdata) {
}
}
-// Returns height of characters in current font
+/**
+* Returns height of characters in current font
+*/
int16 Screen::fontHeight() {
debugC(2, kDebugDisplay, "fontHeight");
@@ -328,8 +359,9 @@ int16 Screen::fontHeight() {
return height[_fnt - FIRST_FONT];
}
-
-// Returns length of supplied string in pixels
+/**
+* Returns length of supplied string in pixels
+*/
int16 Screen::stringLength(const char *s) {
debugC(2, kDebugDisplay, "stringLength(%s)", s);
@@ -341,15 +373,19 @@ int16 Screen::stringLength(const char *s) {
return sum;
}
-// Return x which would center supplied string
+/**
+* Return x which would center supplied string
+*/
int16 Screen::center(const char *s) {
debugC(1, kDebugDisplay, "center(%s)", s);
return (int16)((XPIX - stringLength(s)) >> 1);
}
-// Write string at sx,sy in supplied color in current font
-// If sx == CENTER, center it
+/**
+* Write string at sx,sy in supplied color in current font
+* If sx == CENTER, center it
+*/
void Screen::writeStr(int16 sx, int16 sy, const char *s, byte color) {
debugC(2, kDebugDisplay, "writeStr(%d, %d, %s, %d)", sx, sy, s, color);
@@ -363,7 +399,9 @@ void Screen::writeStr(int16 sx, int16 sy, const char *s, byte color) {
}
}
-// Shadowed version of writestr
+/**
+* Shadowed version of writestr
+*/
void Screen::shadowStr(int16 sx, int16 sy, const char *s, byte color) {
debugC(1, kDebugDisplay, "shadowStr(%d, %d, %s, %d)", sx, sy, s, color);
@@ -374,10 +412,11 @@ void Screen::shadowStr(int16 sx, int16 sy, const char *s, byte color) {
writeStr(sx, sy, s, color);
}
+/** Introduce user to the game
+* DOS versions Only
+*/
void Screen::userHelp() {
-// Introduce user to the game
-// DOS versions Only
- Utils::Box(BOX_ANY , "%s",
+ Utils::Box(BOX_ANY , "%s",
"F1 - Press F1 again\n"
" for instructions\n"
"F2 - Sound on/off\n"
@@ -394,18 +433,18 @@ void Screen::drawStatusText() {
debugC(4, kDebugDisplay, "drawStatusText");
loadFont(U_FONT8);
- uint16 sdx = stringLength(_vm._statusLine);
+ uint16 sdx = stringLength(_vm->_statusLine);
uint16 sdy = fontHeight() + 1; // + 1 for shadow
uint16 posX = 0;
uint16 posY = YPIX - sdy;
// Display the string and add rect to display list
- writeStr(posX, posY, _vm._statusLine, _TLIGHTYELLOW);
+ writeStr(posX, posY, _vm->_statusLine, _TLIGHTYELLOW);
displayList(D_ADD, posX, posY, sdx, sdy);
- sdx = stringLength(_vm._scoreLine);
+ sdx = stringLength(_vm->_scoreLine);
posY = 0;
- writeStr(posX, posY, _vm._scoreLine, _TCYAN);
+ writeStr(posX, posY, _vm->_scoreLine, _TCYAN);
displayList(D_ADD, posX, posY, sdx, sdy);
}
@@ -421,7 +460,7 @@ void Screen::drawShape(int x, int y, int color1, int color2) {
_backBuffer[320 * (y + (2 * shapeSize - 1) - i) + (x + shapeSize + j)] = color2;
_frontBuffer[320 * (y + (2 * shapeSize - 1) - i) + (x + shapeSize + j)] = color2;
}
- }
+ }
}
void Screen::drawRectangle(bool filledFl, uint16 x1, uint16 y1, uint16 x2, uint16 y2, int color) {
@@ -440,5 +479,44 @@ void Screen::drawRectangle(bool filledFl, uint16 x1, uint16 y1, uint16 x2, uint1
}
}
+/**
+* Initialize screen components and display results
+*/
+void Screen::initNewScreenDisplay() {
+ displayList(D_INIT);
+ setBackgroundColor(_TBLACK);
+ displayBackground();
+
+ // Stop premature object display in Display_list(D_DISPLAY)
+ _vm->getGameStatus().newScreenFl = true;
+}
+
+/**
+* Load palette from Hugo.dat
+*/
+void Screen::loadPalette(Common::File &in) {
+ // Read palette
+ _paletteSize = in.readUint16BE();
+ _palette = (byte *)malloc(sizeof(byte) * _paletteSize);
+ for (int i = 0; i < _paletteSize; i++)
+ _palette[i] = in.readByte();
+}
+
+/**
+* Free palette
+*/
+void Screen::freePalette() {
+ free(_palette);
+}
+
+/**
+* Free fonts
+*/
+void Screen::freeFonts() {
+ for (int i = 0; i < NUM_FONTS; i++) {
+ if (_arrayFont[i])
+ free(_arrayFont[i]);
+ }
+}
} // End of namespace Hugo
diff --git a/engines/hugo/display.h b/engines/hugo/display.h
index 16dd87fc6f..ec9f29f016 100644
--- a/engines/hugo/display.h
+++ b/engines/hugo/display.h
@@ -46,9 +46,12 @@ struct rect_t { // Rectangle used in Display
class Screen {
public:
- Screen(HugoEngine &vm);
+ Screen(HugoEngine *vm);
virtual ~Screen();
+ virtual void loadFont(int16 fontId) = 0;
+ virtual void loadFontArr(Common::File &in) = 0;
+
int16 fontHeight();
int16 stringLength(const char *s);
@@ -59,8 +62,11 @@ public:
void drawRectangle(bool filledFl, uint16 x1, uint16 y1, uint16 x2, uint16 y2, int color);
void drawShape(int x, int y, int color1, int color2);
void drawStatusText();
+ void freeFonts();
+ void freePalette();
void initDisplay();
- virtual void loadFont(int16 fontId) = 0;
+ void initNewScreenDisplay();
+ void loadPalette(Common::File &in);
void moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2);
void remapPal(uint16 oldIndex, uint16 newIndex);
void restorePal(Common::SeekableReadStream *f);
@@ -92,12 +98,19 @@ public:
}
protected:
- HugoEngine &_vm;
+ HugoEngine *_vm;
+
+ bool fontLoadedFl[NUM_FONTS];
// Fonts used in dib (non-GDI)
- byte _fnt; // Current font number
- byte _fontdata[NUM_FONTS][FONTSIZE]; // Font data
+ byte *_arrayFont[NUM_FONTS];
+ byte _fnt; // Current font number
+ byte _fontdata[NUM_FONTS][FONTSIZE]; // Font data
byte *_font[NUM_FONTS][FONT_LEN]; // Ptrs to each char
+ byte *_palette;
+ byte _paletteSize;
+
+ int16 _arrayFontSize[NUM_FONTS];
private:
viewdib_t _frontBuffer;
@@ -115,18 +128,20 @@ private:
class Screen_v1d : public Screen {
public:
- Screen_v1d(HugoEngine &vm);
+ Screen_v1d(HugoEngine *vm);
~Screen_v1d();
- virtual void loadFont(int16 fontId);
+ void loadFont(int16 fontId);
+ void loadFontArr(Common::File &in);
};
class Screen_v1w : public Screen {
public:
- Screen_v1w(HugoEngine &vm);
+ Screen_v1w(HugoEngine *vm);
~Screen_v1w();
- virtual void loadFont(int16 fontId);
+ void loadFont(int16 fontId);
+ void loadFontArr(Common::File &in);
};
} // End of namespace Hugo
diff --git a/engines/hugo/display_v1d.cpp b/engines/hugo/display_v1d.cpp
index 6cf20d413f..28c8e4407a 100644
--- a/engines/hugo/display_v1d.cpp
+++ b/engines/hugo/display_v1d.cpp
@@ -40,31 +40,33 @@
namespace Hugo {
-Screen_v1d::Screen_v1d(HugoEngine &vm) : Screen(vm) {
+Screen_v1d::Screen_v1d(HugoEngine *vm) : Screen(vm) {
}
Screen_v1d::~Screen_v1d() {
}
-// Load font file, construct font ptrs and reverse data bytes
-// TODO: This uses hardcoded fonts in hugo.dat, it should be replaced
-// by a proper implementation of .FON files
+/**
+* Load font file, construct font ptrs and reverse data bytes
+* TODO: This uses hardcoded fonts in hugo.dat, it should be replaced
+* by a proper implementation of .FON files
+*/
void Screen_v1d::loadFont(int16 fontId) {
debugC(2, kDebugDisplay, "loadFont(%d)", fontId);
- static bool fontLoadedFl[NUM_FONTS] = {false, false, false};
+ assert(fontId < NUM_FONTS);
_fnt = fontId - FIRST_FONT; // Set current font number
- if (fontLoadedFl[_fnt]) // If already loaded, return
+ if (fontLoadedFl[_fnt]) // If already loaded, return
return;
fontLoadedFl[_fnt] = true;
- memcpy(_fontdata[_fnt], _vm._arrayFont[_fnt], _vm._arrayFontSize[_fnt]);
+ memcpy(_fontdata[_fnt], _arrayFont[_fnt], _arrayFontSize[_fnt]);
_font[_fnt][0] = _fontdata[_fnt]; // Store height,width of fonts
- int16 offset = 2; // Start at fontdata[2] ([0],[1] used for height,width)
+ int16 offset = 2; // Start at fontdata[2] ([0],[1] used for height,width)
// Setup the font array (127 characters)
for (int i = 1; i < 128; i++) {
@@ -79,5 +81,20 @@ void Screen_v1d::loadFont(int16 fontId) {
offset += 2 + size;
}
}
+
+/**
+* Load fonts from Hugo.dat
+* These fonts are a workaround to avoid handling TTF fonts used by DOS versions
+* TODO: Properly handle the vector based font files (win31)
+*/
+void Screen_v1d::loadFontArr(Common::File &in) {
+ for (int i = 0; i < NUM_FONTS; i++) {
+ _arrayFontSize[i] = in.readUint16BE();
+ _arrayFont[i] = (byte *)malloc(sizeof(byte) * _arrayFontSize[i]);
+ for (int j = 0; j < _arrayFontSize[i]; j++) {
+ _arrayFont[i][j] = in.readByte();
+ }
+ }
+}
} // End of namespace Hugo
diff --git a/engines/hugo/display_v1w.cpp b/engines/hugo/display_v1w.cpp
index c98374dcde..7cc94cc85a 100644
--- a/engines/hugo/display_v1w.cpp
+++ b/engines/hugo/display_v1w.cpp
@@ -41,25 +41,25 @@
namespace Hugo {
-Screen_v1w::Screen_v1w(HugoEngine &vm) : Screen(vm) {
+Screen_v1w::Screen_v1w(HugoEngine *vm) : Screen(vm) {
}
Screen_v1w::~Screen_v1w() {
}
-// Load font file, construct font ptrs and reverse data bytes
+/**
+* Load font file, construct font ptrs and reverse data bytes
+*/
void Screen_v1w::loadFont(int16 fontId) {
debugC(2, kDebugDisplay, "loadFont(%d)", fontId);
- static bool fontLoadedFl[NUM_FONTS] = {false, false, false};
-
_fnt = fontId - FIRST_FONT; // Set current font number
if (fontLoadedFl[_fnt]) // If already loaded, return
return;
fontLoadedFl[_fnt] = true;
- _vm.file().readUIFItem(fontId, _fontdata[_fnt]);
+ _vm->_file->readUIFItem(fontId, _fontdata[_fnt]);
// Compile font ptrs. Note: First ptr points to height,width of font
_font[_fnt][0] = _fontdata[_fnt]; // Store height,width of fonts
@@ -79,5 +79,16 @@ void Screen_v1w::loadFont(int16 fontId) {
offset += 2 + size;
}
}
+
+/**
+* Skips the fonts used by the DOS versions
+*/
+void Screen_v1w::loadFontArr(Common::File &in) {
+ for (int i = 0; i < NUM_FONTS; i++) {
+ uint16 numElem = in.readUint16BE();
+ for (int j = 0; j < numElem; j++)
+ in.readByte();
+ }
+}
} // End of namespace Hugo
diff --git a/engines/hugo/engine.cpp b/engines/hugo/engine.cpp
deleted file mode 100644
index d1d3e92cb2..0000000000
--- a/engines/hugo/engine.cpp
+++ /dev/null
@@ -1,968 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on original Hugo 1-3 Trilogy source code
- *
- * Copyright (c) 1989-1995 David P. Gray
- *
- */
-
-#include "common/system.h"
-#include "common/random.h"
-#include "common/EventRecorder.h"
-
-#include "hugo/game.h"
-#include "hugo/hugo.h"
-#include "hugo/engine.h"
-#include "hugo/global.h"
-#include "hugo/file.h"
-#include "hugo/schedule.h"
-#include "hugo/display.h"
-#include "hugo/parser.h"
-#include "hugo/route.h"
-#include "hugo/util.h"
-#include "hugo/sound.h"
-
-namespace Hugo {
-
-#define EDGE 10 // Closest object can get to edge of screen
-#define EDGE2 (EDGE * 2) // Push object further back on edge collision
-#define SHIFT 8 // Place hero this far inside bounding box
-#define MAX_OBJECTS 128 // Used in Update_images()
-#define BOUND(X, Y) ((_boundary[Y * XBYTES + X / 8] & (0x80 >> X % 8)) != 0) // Boundary bit set
-
-config_t _config; // User's config
-maze_t _maze = {false, 0, 0, 0, 0, 0, 0, 0, 0};// Default to not in maze
-hugo_boot_t _boot; // Boot info structure file
-char _textBoxBuffer[MAX_BOX]; // Buffer for text box
-command_t _line = ""; // Line of user text input
-
-
-// Sets the playlist to be the default tune selection
-void HugoEngine::initPlaylist(bool playlist[MAX_TUNES]) {
- debugC(1, kDebugEngine, "initPlaylist");
-
- for (int16 i = 0; i < MAX_TUNES; i++)
- playlist[i] = false;
- for (int16 i = 0; _defltTunes[i] != -1; i++)
- playlist[_defltTunes[i]] = true;
-}
-
-// Initialize the dynamic game status
-void HugoEngine::initStatus() {
- debugC(1, kDebugEngine, "initStatus");
- _status.initSaveFl = true; // Force initial save
- _status.storyModeFl = false; // Not in story mode
- _status.gameOverFl = false; // Hero not knobbled yet
- _status.recordFl = false; // Not record mode
- _status.playbackFl = false; // Not playback mode
- _status.demoFl = false; // Not demo mode
- _status.textBoxFl = false; // Not processing a text box
-// Strangerke - Not used ?
-// _status.mmtime = false; // Multimedia timer support
- _status.lookFl = false; // Toolbar "look" button
- _status.recallFl = false; // Toolbar "recall" button
- _status.leftButtonFl = false; // Left mouse button pressed
- _status.rightButtonFl = false; // Right mouse button pressed
- _status.newScreenFl = false; // Screen not just loaded
- _status.jumpExitFl = false; // Can't jump to a screen exit
- _status.godModeFl = false; // No special cheats allowed
- _status.helpFl = false; // Not calling WinHelp()
- _status.doQuitFl = false;
- _status.path[0] = 0; // Path to write files
- _status.saveSlot = 0; // Slot to save/restore game
- _status.screenWidth = 0; // Desktop screen width
-
- // Initialize every start of new game
- _status.tick = 0; // Tick count
- _status.saveTick = 0; // Time of last save
- _status.viewState = V_IDLE; // View state
- _status.inventoryState = I_OFF; // Inventory icon bar state
- _status.inventoryHeight = 0; // Inventory icon bar pos
- _status.inventoryObjId = -1; // Inventory object selected (none)
- _status.routeIndex = -1; // Hero not following a route
- _status.go_for = GO_SPACE; // Hero walking to space
- _status.go_id = -1; // Hero not walking to anything
-}
-
-// Initialize default config values. Must be done before Initialize().
-// Reset needed to save config.cx,cy which get splatted during OnFileNew()
-void HugoEngine::initConfig(inst_t action) {
- debugC(1, kDebugEngine, "initConfig(%d)", action);
-
- switch (action) {
- case INSTALL:
- _config.musicFl = true; // Music state initially on
- _config.soundFl = true; // Sound state initially on
- _config.turboFl = false; // Turbo state initially off
- _config.backgroundMusicFl = false; // No music when inactive
- _config.musicVolume = 85; // Music volume %
- _config.soundVolume = 100; // Sound volume %
- initPlaylist(_config.playlist); // Initialize default tune playlist
-
- file().readBootFile(); // Read startup structure
- break;
- case RESET:
- // Find first tune and play it
- for (int16 i = 0; i < MAX_TUNES; i++) {
- if (_config.playlist[i]) {
- sound().playMusic(i);
- break;
- }
- }
-
- file().initSavedGame(); // Initialize saved game
- break;
- case RESTORE:
- warning("Unhandled action RESTORE");
- break;
- }
-}
-
-void HugoEngine::initialize() {
- debugC(1, kDebugEngine, "initialize");
-
- sound().initSound();
- scheduler().initEventQueue(); // Init scheduler stuff
- screen().initDisplay(); // Create Dibs and palette
- file().openDatabaseFiles(); // Open database files
- calcMaxScore(); // Initialise maxscore
-
- _rnd = new Common::RandomSource();
- g_eventRec.registerRandomSource(*_rnd, "hugo");
-
- _rnd->setSeed(42); // Kick random number generator
-
- switch (getGameType()) {
- case kGameTypeHugo1:
- _episode = "\"HUGO'S HOUSE OF HORRORS\"";
- _picDir = "";
- break;
- case kGameTypeHugo2:
- _episode = "\"Hugo's Mystery Adventure\"";
- _picDir = "hugo2/";
- break;
- case kGameTypeHugo3:
- _episode = "\"Hugo's Amazon Adventure\"";
- _picDir = "hugo3/";
- break;
- default:
- error("Unknown game");
- }
-}
-
-// Restore all resources before termination
-void HugoEngine::shutdown() {
- debugC(1, kDebugEngine, "shutdown");
-
- file().closeDatabaseFiles();
- if (_status.recordFl || _status.playbackFl)
- file().closePlaybackFile();
- freeObjects();
-}
-
-void HugoEngine::readObjectImages() {
- debugC(1, kDebugEngine, "readObjectImages");
-
- for (int i = 0; i < _numObj; i++)
- file().readImage(i, &_objects[i]);
-}
-
-// Read the uif image file (inventory icons)
-void HugoEngine::readUIFImages() {
- debugC(1, kDebugEngine, "readUIFImages");
-
- file().readUIFItem(UIF_IMAGES, screen().getGUIBuffer()); // Read all uif images
-}
-
-// Read scenery, overlay files for given screen number
-void HugoEngine::readScreenFiles(int screenNum) {
- debugC(1, kDebugEngine, "readScreenFiles(%d)", screenNum);
-
- file().readBackground(screenNum); // Scenery file
- memcpy(screen().getBackBuffer(), screen().getFrontBuffer(), sizeof(screen().getFrontBuffer()));// Make a copy
- file().readOverlay(screenNum, _boundary, BOUNDARY); // Boundary file
- file().readOverlay(screenNum, _overlay, OVERLAY); // Overlay file
- file().readOverlay(screenNum, _ovlBase, OVLBASE); // Overlay base file
-}
-
-// Update all object positions. Process object 'local' events
-// including boundary events and collisions
-void HugoEngine::moveObjects() {
- debugC(4, kDebugEngine, "moveObjects");
-
- // If route mode enabled, do special route processing
- if (_status.routeIndex >= 0)
- route().processRoute();
-
- // Perform any adjustments to velocity based on special path types
- // and store all (visible) object baselines into the boundary file.
- // Don't store foreground or background objects
- for (int i = 0; i < _numObj; i++) {
- object_t *obj = &_objects[i]; // Get pointer to object
- seq_t *currImage = obj->currImagePtr; // Get ptr to current image
- if (obj->screenIndex == *_screen_p) {
- switch (obj->pathType) {
- case CHASE:
- case CHASE2: {
- int8 radius = obj->radius; // Default to object's radius
- if (radius < 0) // If radius infinity, use closer value
- radius = DX;
-
- // Allowable motion wrt boundary
- int dx = _hero->x + _hero->currImagePtr->x1 - obj->x - currImage->x1;
- int dy = _hero->y + _hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
- if (abs(dx) <= radius)
- obj->vx = 0;
- else
- obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
- if (abs(dy) <= radius)
- obj->vy = 0;
- else
- obj->vy = (dy > 0) ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
-
- // Set first image in sequence (if multi-seq object)
- switch (obj->seqNumb) {
- case 4:
- if (!obj->vx) { // Got 4 directions
- if (obj->vx != obj->oldvx) { // vx just stopped
- if (dy >= 0)
- obj->currImagePtr = obj->seqList[DOWN].seqPtr;
- else
- obj->currImagePtr = obj->seqList[_UP].seqPtr;
- }
- } else if (obj->vx != obj->oldvx) {
- if (dx > 0)
- obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
- else
- obj->currImagePtr = obj->seqList[LEFT].seqPtr;
- }
- break;
- case 3:
- case 2:
- if (obj->vx != obj->oldvx) { // vx just stopped
- if (dx > 0) // Left & right only
- obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
- else
- obj->currImagePtr = obj->seqList[LEFT].seqPtr;
- }
- break;
- }
-
- if (obj->vx || obj->vy)
- obj->cycling = CYCLE_FORWARD;
- else {
- obj->cycling = NOT_CYCLING;
- boundaryCollision(obj); // Must have got hero!
- }
- obj->oldvx = obj->vx;
- obj->oldvy = obj->vy;
- currImage = obj->currImagePtr; // Get (new) ptr to current image
- break;
- }
- case WANDER2:
- case WANDER:
- if (!_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
- obj->vx = _rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
- obj->vy = _rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
-
- // Set first image in sequence (if multi-seq object)
- if (obj->seqNumb > 1) {
- if (!obj->vx && (obj->seqNumb >= 4)) {
- if (obj->vx != obj->oldvx) { // vx just stopped
- if (obj->vy > 0)
- obj->currImagePtr = obj->seqList[DOWN].seqPtr;
- else
- obj->currImagePtr = obj->seqList[_UP].seqPtr;
- }
- } else if (obj->vx != obj->oldvx) {
- if (obj->vx > 0)
- obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
- else
- obj->currImagePtr = obj->seqList[LEFT].seqPtr;
- }
- }
- obj->oldvx = obj->vx;
- obj->oldvy = obj->vy;
- currImage = obj->currImagePtr; // Get (new) ptr to current image
- }
- if (obj->vx || obj->vy)
- obj->cycling = CYCLE_FORWARD;
- break;
- default:
- ; // Really, nothing
- }
- // Store boundaries
- if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
- storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
- }
- }
-
- // Move objects, allowing for boundaries
- for (int i = 0; i < _numObj; i++) {
- object_t *obj = &_objects[i]; // Get pointer to object
- if ((obj->screenIndex == *_screen_p) && (obj->vx || obj->vy)) {
- // Only process if it's moving
-
- // Do object movement. Delta_x,y return allowed movement in x,y
- // to move as close to a boundary as possible without crossing it.
- seq_t *currImage = obj->currImagePtr; // Get ptr to current image
- // object coordinates
- int x1 = obj->x + currImage->x1; // Left edge of object
- int x2 = obj->x + currImage->x2; // Right edge
- int y1 = obj->y + currImage->y1; // Top edge
- int y2 = obj->y + currImage->y2; // Bottom edge
-
- if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
- clearBoundary(x1, x2, y2); // Clear our own boundary
-
- // Allowable motion wrt boundary
- int dx = deltaX(x1, x2, obj->vx, y2);
- if (dx != obj->vx) {
- // An object boundary collision!
- boundaryCollision(obj);
- obj->vx = 0;
- }
-
- int dy = deltaY(x1, x2, obj->vy, y2);
- if (dy != obj->vy) {
- // An object boundary collision!
- boundaryCollision(obj);
- obj->vy = 0;
- }
-
- if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
- storeBoundary(x1, x2, y2); // Re-store our own boundary
-
- obj->x += dx; // Update object position
- obj->y += dy;
-
- // Don't let object go outside screen
- if (x1 < EDGE)
- obj->x = EDGE2;
- if (x2 > (XPIX - EDGE))
- obj->x = XPIX - EDGE2 - (x2 - x1);
- if (y1 < EDGE)
- obj->y = EDGE2;
- if (y2 > (YPIX - EDGE))
- obj->y = YPIX - EDGE2 - (y2 - y1);
-
- if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2))
- obj->cycling = NOT_CYCLING;
- }
- }
-
- // Clear all object baselines from the boundary file.
- for (int i = 0; i < _numObj; i++) {
- object_t *obj = &_objects[i]; // Get pointer to object
- seq_t *currImage = obj->currImagePtr; // Get ptr to current image
- if ((obj->screenIndex == *_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
- clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
- }
-
- // If maze mode is enabled, do special maze processing
- if (_maze.enabledFl)
- processMaze();
-}
-
-// Return maximum allowed movement (from zero to vx) such that object does
-// not cross a boundary (either background or another object)
-int HugoEngine::deltaX(int x1, int x2, int vx, int y) {
-// Explanation of algorithm: The boundaries are drawn as contiguous
-// lines 1 pixel wide. Since DX,DY are not necessarily 1, we must
-// detect boundary crossing. If vx positive, examine each pixel from
-// x1 old to x2 new, else x2 old to x1 new, both at the y2 line.
-// If vx zero, no need to check. If vy non-zero then examine each
-// pixel on the line segment x1 to x2 from y old to y new.
-// Fix from Hugo I v1.5:
-// Note the diff is munged in the return statement to cater for a special
-// cases arising from differences in image widths from one sequence to
-// another. The problem occurs reversing direction at a wall where the
-// new image intersects before the object can move away. This is cured
-// by comparing the intersection with half the object width pos. If the
-// intersection is in the other half wrt the intended direction, use the
-// desired vx, else use the computed delta. i.e. believe the desired vx
-
- debugC(3, kDebugEngine, "deltaX(%d, %d, %d, %d)", x1, x2, vx, y);
-
- if (vx == 0)
- return 0 ; // Object stationary
-
- y *= XBYTES; // Offset into boundary file
- if (vx > 0) {
- // Moving to right
- for (int i = x1 >> 3; i <= (x2 + vx) >> 3; i++) {// Search by byte
- int b = Utils::firstBit((byte)(_boundary[y + i] | _objBound[y + i]));
- if (b < 8) { // b is index or 8
- // Compute x of boundary and test if intersection
- b += i << 3;
- if ((b >= x1) && (b <= x2 + vx))
- return (b < x1 + ((x2 - x1) >> 1)) ? vx : b - x2 - 1; // return dx
- }
- }
- } else {
- // Moving to left
- for (int i = x2 >> 3; i >= (x1 + vx) >> 3; i--) {// Search by byte
- int b = Utils::lastBit((byte)(_boundary[y + i] | _objBound[y + i]));
- if (b < 8) { // b is index or 8
- // Compute x of boundary and test if intersection
- b += i << 3;
- if ((b >= x1 + vx) && (b <= x2))
- return (b > x1 + ((x2 - x1) >> 1)) ? vx : b - x1 + 1; // return dx
- }
- }
- }
- return vx;
-}
-
-// Similar to Delta_x, but for movement in y direction. Special case of
-// bytes at end of line segment; must only count boundary bits falling on
-// line segment.
-int HugoEngine::deltaY(int x1, int x2, int vy, int y) {
- debugC(3, kDebugEngine, "deltaY(%d, %d, %d, %d)", x1, x2, vy, y);
-
- if (vy == 0)
- return 0; // Object stationary
-
- int inc = (vy > 0) ? 1 : -1;
- for (int j = y + inc; j != (y + vy + inc); j += inc) { //Search by byte
- for (int i = x1 >> 3; i <= x2 >> 3; i++) {
- int b = _boundary[j * XBYTES + i] | _objBound[j * XBYTES + i];
- if (b != 0) { // Any bit set
- // Make sure boundary bits fall on line segment
- if (i == (x2 >> 3)) // Adjust right end
- b &= 0xff << ((i << 3) + 7 - x2);
- else if (i == (x1 >> 3)) // Adjust left end
- b &= 0xff >> (x1 - (i << 3));
- if (b)
- return j - y - inc;
- }
- }
- }
- return vy;
-}
-
-// Store a horizontal line segment in the object boundary file
-void HugoEngine::storeBoundary(int x1, int x2, int y) {
- debugC(5, kDebugEngine, "storeBoundary(%d, %d, %d)", x1, x2, y);
-
- for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
- byte *b = &_objBound[y * XBYTES + i]; // get boundary byte
- if (i == x2 >> 3) // Adjust right end
- *b |= 0xff << ((i << 3) + 7 - x2);
- else if (i == x1 >> 3) // Adjust left end
- *b |= 0xff >> (x1 - (i << 3));
- else
- *b = 0xff;
- }
-}
-
-// Clear a horizontal line segment in the object boundary file
-void HugoEngine::clearBoundary(int x1, int x2, int y) {
- debugC(5, kDebugEngine, "clearBoundary(%d, %d, %d)", x1, x2, y);
-
- for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
- byte *b = &_objBound[y * XBYTES + i]; // get boundary byte
- if (i == x2 >> 3) // Adjust right end
- *b &= ~(0xff << ((i << 3) + 7 - x2));
- else if (i == x1 >> 3) // Adjust left end
- *b &= ~(0xff >> (x1 - (i << 3)));
- else
- *b = 0;
- }
-}
-
-// Maze mode is enabled. Check to see whether hero has crossed the maze
-// bounding box, if so, go to the next room */
-void HugoEngine::processMaze() {
- debugC(1, kDebugEngine, "processMaze");
-
- seq_t *currImage = _hero->currImagePtr; // Get ptr to current image
-
- // hero coordinates
- int x1 = _hero->x + currImage->x1; // Left edge of object
- int x2 = _hero->x + currImage->x2; // Right edge
- int y1 = _hero->y + currImage->y1; // Top edge
- int y2 = _hero->y + currImage->y2; // Bottom edge
-
- if (x1 < _maze.x1) {
- // Exit west
- _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - 1;
- _actListArr[_alNewscrIndex][0].a2.x = _maze.x2 - SHIFT - (x2 - x1);
- _actListArr[_alNewscrIndex][0].a2.y = _hero->y;
- _status.routeIndex = -1;
- scheduler().insertActionList(_alNewscrIndex);
- } else if (x2 > _maze.x2) {
- // Exit east
- _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + 1;
- _actListArr[_alNewscrIndex][0].a2.x = _maze.x1 + SHIFT;
- _actListArr[_alNewscrIndex][0].a2.y = _hero->y;
- _status.routeIndex = -1;
- scheduler().insertActionList(_alNewscrIndex);
- } else if (y1 < _maze.y1 - SHIFT) {
- // Exit north
- _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - _maze.size;
- _actListArr[_alNewscrIndex][0].a2.x = _maze.x3;
- _actListArr[_alNewscrIndex][0].a2.y = _maze.y2 - SHIFT - (y2 - y1);
- _status.routeIndex = -1;
- scheduler().insertActionList(_alNewscrIndex);
- } else if (y2 > _maze.y2 - SHIFT / 2) {
- // Exit south
- _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + _maze.size;
- _actListArr[_alNewscrIndex][0].a2.x = _maze.x4;
- _actListArr[_alNewscrIndex][0].a2.y = _maze.y1 + SHIFT;
- _status.routeIndex = -1;
- scheduler().insertActionList(_alNewscrIndex);
- }
-}
-
-// Compare function for the quicksort. The sort is to order the objects in
-// increasing vertical position, using y+y2 as the baseline
-// Returns -1 if ay2 < by2 else 1 if ay2 > by2 else 0
-int HugoEngine::y2comp(const void *a, const void *b) {
- debugC(6, kDebugEngine, "y2comp");
-
- const object_t *p1 = &s_Engine->_objects[*(const byte *)a];
- const object_t *p2 = &s_Engine->_objects[*(const byte *)b];
-
- if (p1 == p2)
- // Why does qsort try the same indexes?
- return 0;
-
- if (p1->priority == BACKGROUND)
- return -1;
-
- if (p2->priority == BACKGROUND)
- return 1;
-
- if (p1->priority == FOREGROUND)
- return 1;
-
- if (p2->priority == FOREGROUND)
- return -1;
-
- int ay2 = p1->y + p1->currImagePtr->y2;
- int by2 = p2->y + p2->currImagePtr->y2;
-
- return ay2 - by2;
-}
-
-// Draw all objects on screen as follows:
-// 1. Sort 'FLOATING' objects in order of y2 (base of object)
-// 2. Display new object frames/positions in dib
-// Finally, cycle any animating objects to next frame
-void HugoEngine::updateImages() {
- debugC(5, kDebugEngine, "updateImages");
-
- // Initialise the index array to visible objects in current screen
- int num_objs = 0;
- byte objindex[MAX_OBJECTS]; // Array of indeces to objects
-
- for (int i = 0; i < _numObj; i++) {
- object_t *obj = &_objects[i];
- if ((obj->screenIndex == *_screen_p) && (obj->cycling >= ALMOST_INVISIBLE))
- objindex[num_objs++] = i;
- }
-
- // Sort the objects into increasing y+y2 (painter's algorithm)
- qsort(objindex, num_objs, sizeof(objindex[0]), y2comp);
-
- // Add each visible object to display list
- for (int i = 0; i < num_objs; i++) {
- object_t *obj = &_objects[objindex[i]];
- // Count down inter-frame timer
- if (obj->frameTimer)
- obj->frameTimer--;
-
- if (obj->cycling > ALMOST_INVISIBLE) { // Only if visible
- switch (obj->cycling) {
- case NOT_CYCLING:
- screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
- break;
- case CYCLE_FORWARD:
- if (obj->frameTimer) // Not time to see next frame yet
- screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
- else
- screen().displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, obj->priority == OVEROVL);
- break;
- case CYCLE_BACKWARD: {
- seq_t *seqPtr = obj->currImagePtr;
- if (!obj->frameTimer) { // Show next frame
- while (seqPtr->nextSeqPtr != obj->currImagePtr)
- seqPtr = seqPtr->nextSeqPtr;
- }
- screen().displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL);
- break;
- }
- default:
- break;
- }
- }
- }
-
- // Cycle any animating objects
- for (int i = 0; i < num_objs; i++) {
- object_t *obj = &_objects[objindex[i]];
- if (obj->cycling != INVISIBLE) {
- // Only if it's visible
- if (obj->cycling == ALMOST_INVISIBLE)
- obj->cycling = INVISIBLE;
-
- // Now Rotate to next picture in sequence
- switch (obj->cycling) {
- case NOT_CYCLING:
- break;
- case CYCLE_FORWARD:
- if (!obj->frameTimer) {
- // Time to step to next frame
- obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
- // Find out if this is last frame of sequence
- // If so, reset frame_timer and decrement n_cycle
- if (obj->frameInterval || obj->cycleNumb) {
- obj->frameTimer = obj->frameInterval;
- for (int j = 0; j < obj->seqNumb; j++) {
- if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr) {
- if (obj->cycleNumb) { // Decr cycleNumb if Non-continous
- if (!--obj->cycleNumb)
- obj->cycling = NOT_CYCLING;
- }
- }
- }
- }
- }
- break;
- case CYCLE_BACKWARD: {
- if (!obj->frameTimer) {
- // Time to step to prev frame
- seq_t *seqPtr = obj->currImagePtr;
- while (obj->currImagePtr->nextSeqPtr != seqPtr)
- obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
- // Find out if this is first frame of sequence
- // If so, reset frame_timer and decrement n_cycle
- if (obj->frameInterval || obj->cycleNumb) {
- obj->frameTimer = obj->frameInterval;
- for (int j = 0; j < obj->seqNumb; j++) {
- if (obj->currImagePtr == obj->seqList[j].seqPtr) {
- if (obj->cycleNumb){ // Decr cycleNumb if Non-continous
- if (!--obj->cycleNumb)
- obj->cycling = NOT_CYCLING;
- }
- }
- }
- }
- }
- break;
- }
- default:
- break;
- }
- obj->oldx = obj->x;
- obj->oldy = obj->y;
- }
- }
-}
-
-// Return object index of the topmost object under the cursor, or -1 if none
-// Objects are filtered if not "useful"
-int16 HugoEngine::findObject(uint16 x, uint16 y) {
- debugC(3, kDebugEngine, "findObject(%d, %d)", x, y);
-
- int16 objIndex = -1; // Index of found object
- uint16 y2Max = 0; // Greatest y2
- object_t *obj = _objects;
- // Check objects on screen
- for (int i = 0; i < _numObj; i++, obj++) {
- // Object must be in current screen and "useful"
- if (obj->screenIndex == *_screen_p && (obj->genericCmd || obj->objValue || obj->cmdIndex)) {
- seq_t *curImage = obj->currImagePtr;
- // Object must have a visible image...
- if (curImage != 0 && obj->cycling != INVISIBLE) {
- // If cursor inside object
- if (x >= (uint16)obj->x && x <= obj->x + curImage->x2 && y >= (uint16)obj->y && y <= obj->y + curImage->y2) {
- // If object is closest so far
- if (obj->y + curImage->y2 > y2Max) {
- y2Max = obj->y + curImage->y2;
- objIndex = i; // Found an object!
- }
- }
- } else {
- // ...or a dummy object that has a hotspot rectangle
- if (curImage == 0 && obj->vxPath != 0 && !obj->carriedFl) {
- // If cursor inside special rectangle
- if ((int16)x >= obj->oldx && (int16)x < obj->oldx + obj->vxPath && (int16)y >= obj->oldy && (int16)y < obj->oldy + obj->vyPath) {
- // If object is closest so far
- if (obj->oldy + obj->vyPath - 1 > (int16)y2Max) {
- y2Max = obj->oldy + obj->vyPath - 1;
- objIndex = i; // Found an object!
- }
- }
- }
- }
- }
- }
- return objIndex;
-}
-
-// Find a clear space around supplied object that hero can walk to
-bool HugoEngine::findObjectSpace(object_t *obj, int16 *destx, int16 *desty) {
- debugC(1, kDebugEngine, "findObjectSpace(obj, %d, %d)", *destx, *desty);
-
- seq_t *curImage = obj->currImagePtr;
- int16 y = obj->y + curImage->y2 - 1;
-
- bool foundFl = true;
- // Try left rear corner
- for (int16 x = *destx = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) {
- if (BOUND(x, y))
- foundFl = false;
- }
-
- if (!foundFl) { // Try right rear corner
- foundFl = true;
- for (int16 x = *destx = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) {
- if (BOUND(x, y))
- foundFl = false;
- }
- }
-
- if (!foundFl) { // Try left front corner
- foundFl = true;
- y += 2;
- for (int16 x = *destx = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) {
- if (BOUND(x, y))
- foundFl = false;
- }
- }
-
- if (!foundFl) { // Try right rear corner
- foundFl = true;
- for (int16 x = *destx = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) {
- if (BOUND(x, y))
- foundFl = false;
- }
- }
-
- *desty = y;
- return foundFl;
-}
-
-// Search background command list for this screen for supplied object.
-// Return first associated verb (not "look") or 0 if none found.
-char *HugoEngine::useBG(char *name) {
- debugC(1, kDebugEngine, "useBG(%s)", name);
-
- objectList_t p = _backgroundObjects[*_screen_p];
- for (int i = 0; *_arrayVerbs[p[i].verbIndex]; i++) {
- if ((name == _arrayNouns[p[i].nounIndex][0] &&
- p[i].verbIndex != _look) &&
- ((p[i].roomState == DONT_CARE) || (p[i].roomState == _screenStates[*_screen_p])))
- return _arrayVerbs[p[i].verbIndex][0];
- }
-
- return 0;
-}
-
-// If status.objid = -1, pick up objid, else use status.objid on objid,
-// if objid can't be picked up, use it directly
-void HugoEngine::useObject(int16 objId) {
- debugC(1, kDebugEngine, "useObject(%d)", objId);
-
- char *verb; // Background verb to use directly
- object_t *obj = &_objects[objId]; // Ptr to object
- if (_status.inventoryObjId == -1) {
- // Get or use objid directly
- if ((obj->genericCmd & TAKE) || obj->objValue) // Get collectible item
- sprintf(_line, "%s %s", _arrayVerbs[_take][0], _arrayNouns[obj->nounIndex][0]);
- else if (obj->genericCmd & LOOK) // Look item
- sprintf(_line, "%s %s", _arrayVerbs[_look][0], _arrayNouns[obj->nounIndex][0]);
- else if (obj->genericCmd & DROP) // Drop item
- sprintf(_line, "%s %s", _arrayVerbs[_drop][0], _arrayNouns[obj->nounIndex][0]);
- else if (obj->cmdIndex != 0) // Use non-collectible item if able
- sprintf(_line, "%s %s", _arrayVerbs[_cmdList[obj->cmdIndex][1].verbIndex][0], _arrayNouns[obj->nounIndex][0]);
- else if ((verb = useBG(_arrayNouns[obj->nounIndex][0])) != 0)
- sprintf(_line, "%s %s", verb, _arrayNouns[obj->nounIndex][0]);
- else
- return; // Can't use object directly
- } else {
- // Use status.objid on objid
- // Default to first cmd verb
- sprintf(_line, "%s %s %s", _arrayVerbs[_cmdList[_objects[_status.inventoryObjId].cmdIndex][1].verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]);
-
- // Check valid use of objects and override verb if necessary
- for (uses_t *use = _uses; use->objId != _numObj; use++) {
- if (_status.inventoryObjId == use->objId) {
- // Look for secondary object, if found use matching verb
- bool foundFl = false;
- for (target_t *target = use->targets; _arrayNouns[target->nounIndex] != 0; target++)
- if (_arrayNouns[target->nounIndex][0] == _arrayNouns[obj->nounIndex][0]) {
- foundFl = true;
- sprintf(_line, "%s %s %s", _arrayVerbs[target->verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]);
- }
-
- // No valid use of objects found, print failure string
- if (!foundFl) {
- // Deselect dragged icon if inventory not active
- if (_status.inventoryState != I_ACTIVE)
- _status.inventoryObjId = -1;
- Utils::Box(BOX_ANY, "%s", _textData[use->dataIndex]);
- return;
- }
- }
- }
- }
-
- if (_status.inventoryState == I_ACTIVE) // If inventory active, remove it
- _status.inventoryState = I_UP;
- _status.inventoryObjId = -1; // Deselect any dragged icon
- parser().lineHandler(); // and process command
-}
-
-// Issue "Look at <object>" command
-// Note special case of swapped hero image
-void HugoEngine::lookObject(object_t *obj) {
- debugC(1, kDebugEngine, "lookObject");
-
- if (obj == _hero)
- // Hero swapped - look at other
- obj = &_objects[_heroImage];
-
- parser().command("%s %s", _arrayVerbs[_look][0], _arrayNouns[obj->nounIndex][0]);
-}
-
-// Free all object images
-void HugoEngine::freeObjects() {
- debugC(1, kDebugEngine, "freeObjects");
-
- // Nothing to do if not allocated yet
- if (_hero->seqList[0].seqPtr == 0)
- return;
-
- // Free all sequence lists and image data
- for (int i = 0; i < _numObj; i++) {
- object_t *obj = &_objects[i];
- for (int j = 0; j < obj->seqNumb; j++) { // for each sequence
- seq_t *seq = obj->seqList[j].seqPtr; // Free image
- if (seq == 0) // Failure during database load
- break;
- do {
- free(seq->imagePtr);
- seq = seq->nextSeqPtr;
- } while (seq != obj->seqList[j].seqPtr);
- free(seq); // Free sequence record
- }
- }
-}
-
-// Add action lists for this screen to event queue
-void HugoEngine::screenActions(int screenNum) {
- debugC(1, kDebugEngine, "screenActions(%d)", screenNum);
-
- uint16 *screenAct = _screenActs[screenNum];
- if (screenAct) {
- for (int i = 0; screenAct[i]; i++)
- scheduler().insertActionList(screenAct[i]);
- }
-}
-
-// Set the new screen number into the hero object and any carried objects
-void HugoEngine::setNewScreen(int screenNum) {
- debugC(1, kDebugEngine, "setNewScreen(%d)", screenNum);
-
- *_screen_p = screenNum; // HERO object
- for (int i = HERO + 1; i < _numObj; i++) { // Any others
- if (_objects[i].carriedFl) // being carried
- _objects[i].screenIndex = screenNum;
- }
-}
-
-// An object has collided with a boundary. See if any actions are required
-void HugoEngine::boundaryCollision(object_t *obj) {
- debugC(1, kDebugEngine, "boundaryCollision");
-
- if (obj == _hero) {
- // Hotspots only relevant to HERO
- int x;
- if (obj->vx > 0)
- x = obj->x + obj->currImagePtr->x2;
- else
- x = obj->x + obj->currImagePtr->x1;
- int y = obj->y + obj->currImagePtr->y2;
-
- for (int i = 0; _hotspots[i].screenIndex >= 0; i++) {
- hotspot_t *hotspot = &_hotspots[i];
- if (hotspot->screenIndex == obj->screenIndex)
- if ((x >= hotspot->x1) && (x <= hotspot->x2) && (y >= hotspot->y1) && (y <= hotspot->y2)) {
- scheduler().insertActionList(hotspot->actIndex);
- break;
- }
- }
- } else {
- // Check whether an object collided with HERO
- int dx = _hero->x + _hero->currImagePtr->x1 - obj->x - obj->currImagePtr->x1;
- int dy = _hero->y + _hero->currImagePtr->y2 - obj->y - obj->currImagePtr->y2;
- // If object's radius is infinity, use a closer value
- int8 radius = obj->radius;
- if (radius < 0)
- radius = DX * 2;
- if ((abs(dx) <= radius) && (abs(dy) <= radius))
- scheduler().insertActionList(obj->actIndex);
- }
-}
-
-// Initialize screen components and display results
-void HugoEngine::initNewScreenDisplay() {
- debugC(1, kDebugEngine, "initNewScreenDisplay");
-
- screen().displayList(D_INIT);
- screen().setBackgroundColor(_TBLACK);
- screen().displayBackground();
-
- // Stop premature object display in Display_list(D_DISPLAY)
- _status.newScreenFl = true;
-}
-
-// Add up all the object values and all the bonus points
-void HugoEngine::calcMaxScore() {
- debugC(1, kDebugEngine, "calcMaxScore");
-
- for (int i = 0; i < _numObj; i++)
- _maxscore += _objects[i].objValue;
-
- for (int i = 0; i < _numBonuses; i++)
- _maxscore += _points[i].score;
-}
-
-// Exit game, advertise trilogy, show copyright
-void HugoEngine::endGame() {
- debugC(1, kDebugEngine, "endGame");
-
- if (!_boot.registered)
- Utils::Box(BOX_ANY, "%s", _textEngine[kEsAdvertise]);
- Utils::Box(BOX_ANY, "%s\n%s", _episode, COPYRIGHT);
- _status.viewState = V_EXIT;
-}
-
-} // End of namespace Hugo
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
index fa8d5b9947..78bcc46e85 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -39,17 +39,20 @@
#include "hugo/schedule.h"
#include "hugo/display.h"
#include "hugo/util.h"
+#include "hugo/object.h"
namespace Hugo {
-FileManager::FileManager(HugoEngine &vm) : _vm(vm) {
+FileManager::FileManager(HugoEngine *vm) : _vm(vm) {
}
FileManager::~FileManager() {
}
+/**
+* Convert 4 planes (RGBI) data to 8-bit DIB format
+* Return original plane data ptr
+*/
byte *FileManager::convertPCC(byte *p, uint16 y, uint16 bpl, image_pt dataPtr) {
-// Convert 4 planes (RGBI) data to 8-bit DIB format
-// Return original plane data ptr
debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl);
dataPtr += y * bpl * 8; // Point to correct DIB line
@@ -64,10 +67,12 @@ byte *FileManager::convertPCC(byte *p, uint16 y, uint16 bpl, image_pt dataPtr) {
return p;
}
+/**
+* Read a pcx file of length len. Use supplied seq_p and image_p or
+* allocate space if NULL. Name used for errors. Returns address of seq_p
+* Set first TRUE to initialize b_index (i.e. not reading a sequential image in file).
+*/
seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name) {
-// Read a pcx file of length len. Use supplied seq_p and image_p or
-// allocate space if NULL. Name used for errors. Returns address of seq_p
-// Set first TRUE to initialize b_index (i.e. not reading a sequential image in file).
debugC(1, kDebugFile, "readPCX(..., %s)", name);
// Read in the PCC header and check consistency
@@ -89,14 +94,14 @@ seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool
f.read(PCC_header.fill2, sizeof(PCC_header.fill2));
if (PCC_header.mfctr != 10)
- Utils::Error(PCCH_ERR, "%s", name);
+ error("Bad data file format: %s", name);
// Allocate memory for seq_t if 0
if (seqPtr == 0) {
if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == 0)
- Utils::Error(HEAP_ERR, "%s", name);
+ error("Insufficient memory to run game.");
}
-
+
// Find size of image data in 8-bit DIB format
// Note save of x2 - marks end of valid data before garbage
uint16 bytesPerLine4 = PCC_header.bytesPerLine * 4; // 4-bit bpl
@@ -108,9 +113,9 @@ seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool
// Allocate memory for image data if NULL
if (imagePtr == 0) {
- if ((imagePtr = (byte *)malloc((size_t) size)) == 0)
- Utils::Error(HEAP_ERR, "%s", name);
+ imagePtr = (byte *)malloc((size_t) size);
}
+ assert(imagePtr);
seqPtr->imagePtr = imagePtr;
@@ -136,14 +141,24 @@ seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool
return seqPtr;
}
+/**
+* Read object file of PCC images into object supplied
+*/
void FileManager::readImage(int objNum, object_t *objPtr) {
-// Read object file of PCC images into object supplied
debugC(1, kDebugFile, "readImage(%d, object_t *objPtr)", objNum);
+ /**
+ * Structure of object file lookup entry
+ */
+ struct objBlock_t {
+ uint32 objOffset;
+ uint32 objLength;
+ };
+
if (!objPtr->seqNumb) // This object has no images
return;
- if (_vm.isPacked()) {
+ if (_vm->isPacked()) {
_objectsArchive.seek((uint32)objNum * sizeof(objBlock_t), SEEK_SET);
objBlock_t objBlock; // Info on file within database
@@ -153,13 +168,14 @@ void FileManager::readImage(int objNum, object_t *objPtr) {
_objectsArchive.seek(objBlock.objOffset, SEEK_SET);
} else {
char *buf = (char *) malloc(2048 + 1); // Buffer for file access
- strcat(strcat(strcpy(buf, _vm._picDir), _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+ strcat(strcat(strcpy(buf, _vm->_picDir), _vm->_arrayNouns[objPtr->nounIndex][0]), OBJEXT);
if (!_objectsArchive.open(buf)) {
- warning("File %s not found, trying again with %s%s", buf, _vm._arrayNouns[objPtr->nounIndex][0], OBJEXT);
- strcat(strcpy(buf, _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+ warning("File %s not found, trying again with %s%s", buf, _vm->_arrayNouns[objPtr->nounIndex][0], OBJEXT);
+ strcat(strcpy(buf, _vm->_arrayNouns[objPtr->nounIndex][0]), OBJEXT);
if (!_objectsArchive.open(buf))
- Utils::Error(FILE_ERR, "%s", buf);
+ error("File not found: %s", buf);
}
+ free(buf);
}
bool firstFl = true; // Initializes pcx read function
@@ -170,12 +186,12 @@ void FileManager::readImage(int objNum, object_t *objPtr) {
for (int k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image
if (k == 0) { // First image
// Read this image - allocate both seq and image memory
- seqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ seqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm->_arrayNouns[objPtr->nounIndex][0]);
objPtr->seqList[j].seqPtr = seqPtr;
firstFl = false;
} else { // Subsequent image
// Read this image - allocate both seq and image memory
- seqPtr->nextSeqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ seqPtr->nextSeqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm->_arrayNouns[objPtr->nounIndex][0]);
seqPtr = seqPtr->nextSeqPtr;
}
@@ -203,6 +219,7 @@ void FileManager::readImage(int objNum, object_t *objPtr) {
}
}
}
+ assert(seqPtr);
seqPtr->nextSeqPtr = objPtr->seqList[j].seqPtr; // loop linked list to head
}
@@ -221,17 +238,19 @@ void FileManager::readImage(int objNum, object_t *objPtr) {
warning("Unexpected cycling: %d", objPtr->cycling);
}
- if (!_vm.isPacked())
+ if (!_vm->isPacked())
_objectsArchive.close();
}
+/**
+* Read sound (or music) file data. Call with SILENCE to free-up
+* any allocated memory. Also returns size of data
+*/
sound_pt FileManager::getSound(int16 sound, uint16 *size) {
-// Read sound (or music) file data. Call with SILENCE to free-up
-// any allocated memory. Also returns size of data
debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size);
// No more to do if SILENCE (called for cleanup purposes)
- if (sound == _vm._soundSilence)
+ if (sound == _vm->_soundSilence)
return 0;
// Open sounds file
@@ -247,163 +266,129 @@ sound_pt FileManager::getSound(int16 sound, uint16 *size) {
if (!has_read_header) {
if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr))
- Utils::Error(FILE_ERR, "%s", SOUND_FILE);
+ error("Wrong sound file format: %s", SOUND_FILE);
has_read_header = true;
}
*size = s_hdr[sound].size;
if (*size == 0)
- Utils::Error(SOUND_ERR, "%s", SOUND_FILE);
+ error("Wrong sound file format or missing sound %d: %s", sound, SOUND_FILE);
// Allocate memory for sound or music, if possible
sound_pt soundPtr = (byte *)malloc(s_hdr[sound].size); // Ptr to sound data
- if (soundPtr == 0) {
- Utils::Warn("%s", "Low on memory");
- return 0;
- }
+ assert(soundPtr);
// Seek to data and read it
fp.seek(s_hdr[sound].offset, SEEK_SET);
if (fp.read(soundPtr, s_hdr[sound].size) != s_hdr[sound].size)
- Utils::Error(FILE_ERR, "%s", SOUND_FILE);
+ error("File not found: %s", SOUND_FILE);
fp.close();
return soundPtr;
}
+/**
+* Return whether file exists or not
+*/
bool FileManager::fileExists(char *filename) {
-// Return whether file exists or not
Common::File f;
- if (f.open(filename)) {
- f.close();
- return true;
- }
- return false;
-}
-
-void FileManager::saveSeq(object_t *obj) {
-// Save sequence number and image number in given object
- debugC(1, kDebugFile, "saveSeq");
-
- bool found = false;
- for (int j = 0; !found && (j < obj->seqNumb); j++) {
- seq_t *q = obj->seqList[j].seqPtr;
- for (int k = 0; !found && (k < obj->seqList[j].imageNbr); k++) {
- if (obj->currImagePtr == q) {
- found = true;
- obj->curSeqNum = j;
- obj->curImageNum = k;
- } else {
- q = q->nextSeqPtr;
- }
- }
- }
-}
-
-void FileManager::restoreSeq(object_t *obj) {
-// Set up cur_seq_p from stored sequence and image number in object
- debugC(1, kDebugFile, "restoreSeq");
-
- seq_t *q = obj->seqList[obj->curSeqNum].seqPtr;
- for (int j = 0; j < obj->curImageNum; j++)
- q = q->nextSeqPtr;
- obj->currImagePtr = q;
+ return(f.exists(filename));
}
+/**
+* Save game to supplied slot
+*/
void FileManager::saveGame(int16 slot, const char *descrip) {
-// Save game to supplied slot (-1 is INITFILE)
debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip);
// Get full path of saved game file - note test for INITFILE
- Common::String path; // Full path of saved game
-
- if (slot == -1)
- path = _vm._initFilename;
- else
- path = Common::String::printf(_vm._saveFilename.c_str(), slot);
-
- Common::WriteStream *out = _vm.getSaveFileManager()->openForSaving(path);
+ Common::String path = Common::String::format(_vm->_saveFilename.c_str(), slot);
+ Common::WriteStream *out = _vm->getSaveFileManager()->openForSaving(path);
if (!out) {
warning("Can't create file '%s', game not saved", path.c_str());
return;
}
// Write version. We can't restore from obsolete versions
- out->write(&kSavegameVersion, sizeof(kSavegameVersion));
+ out->writeByte(kSavegameVersion);
// Save description of saved game
out->write(descrip, DESCRIPLEN);
- // Save objects
- for (int i = 0; i < _vm._numObj; i++) {
- // Save where curr_seq_p is pointing to
- saveSeq(&_vm._objects[i]);
- out->write(&_vm._objects[i], sizeof(object_t));
- }
+ _vm->_object->saveObjects(out);
- const status_t &gameStatus = _vm.getGameStatus();
+ const status_t &gameStatus = _vm->getGameStatus();
// Save whether hero image is swapped
- out->write(&_vm._heroImage, sizeof(_vm._heroImage));
+ out->writeByte(_vm->_heroImage);
// Save score
- int score = _vm.getScore();
- out->write(&score, sizeof(score));
+ out->writeSint16BE(_vm->getScore());
// Save story mode
- out->write(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+ out->writeByte((gameStatus.storyModeFl) ? 1 : 0);
// Save jumpexit mode
- out->write(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+ out->writeByte((gameStatus.jumpExitFl) ? 1 : 0);
// Save gameover status
- out->write(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+ out->writeByte((gameStatus.gameOverFl) ? 1 : 0);
// Save screen states
- out->write(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+ for (int i = 0; i < _vm->_numScreens; i++)
+ out->writeByte(_vm->_screenStates[i]);
// Save points table
- out->write(_vm._points, sizeof(point_t) * _vm._numBonuses);
+ for (int i = 0; i < _vm->_numBonuses; i++) {
+ out->writeByte(_vm->_points[i].score);
+ out->writeByte((_vm->_points[i].scoredFl) ? 1 : 0);
+ }
// Now save current time and all current events in event queue
- _vm.scheduler().saveEvents(out);
+ _vm->_scheduler->saveEvents(out);
// Save palette table
- _vm.screen().savePal(out);
+ _vm->_screen->savePal(out);
// Save maze status
- out->write(&_maze, sizeof(maze_t));
+ out->writeByte((_maze.enabledFl) ? 1 : 0);
+ out->writeByte(_maze.size);
+ out->writeSint16BE(_maze.x1);
+ out->writeSint16BE(_maze.y1);
+ out->writeSint16BE(_maze.x2);
+ out->writeSint16BE(_maze.y2);
+ out->writeSint16BE(_maze.x3);
+ out->writeSint16BE(_maze.x4);
+ out->writeByte(_maze.firstScreenIndex);
out->finalize();
delete out;
}
+/**
+* Restore game from supplied slot number
+*/
void FileManager::restoreGame(int16 slot) {
-// Restore game from supplied slot number (-1 is INITFILE)
debugC(1, kDebugFile, "restoreGame(%d)", slot);
// Initialize new-game status
- _vm.initStatus();
+ _vm->initStatus();
// Get full path of saved game file - note test for INITFILE
Common::String path; // Full path of saved game
- if (slot == -1)
- path = _vm._initFilename;
- else
- path = Common::String::printf(_vm._saveFilename.c_str(), slot);
+ path = Common::String::format(_vm->_saveFilename.c_str(), slot);
- Common::SeekableReadStream *in = _vm.getSaveFileManager()->openForLoading(path);
+ Common::SeekableReadStream *in = _vm->getSaveFileManager()->openForLoading(path);
if (!in)
return;
// Check version, can't restore from different versions
- int saveVersion;
- in->read(&saveVersion, sizeof(saveVersion));
+ int saveVersion = in->readByte();
if (saveVersion != kSavegameVersion) {
- Utils::Error(GEN_ERR, "%s", "Savegame of incompatible version");
+ error("Savegame of incompatible version");
return;
}
@@ -411,120 +396,72 @@ void FileManager::restoreGame(int16 slot) {
in->seek(DESCRIPLEN, SEEK_CUR);
// If hero image is currently swapped, swap it back before restore
- if (_vm._heroImage != HERO)
- _vm.scheduler().swapImages(HERO, _vm._heroImage);
-
- // Restore objects, retain current seqList which points to dynamic mem
- // Also, retain cmnd_t pointers
- for (int i = 0; i < _vm._numObj; i++) {
- object_t *p = &_vm._objects[i];
- seqList_t seqList[MAX_SEQUENCES];
- memcpy(seqList, p->seqList, sizeof(seqList_t));
- uint16 cmdIndex = p->cmdIndex;
- in->read(p, sizeof(object_t));
- p->cmdIndex = cmdIndex;
- memcpy(p->seqList, seqList, sizeof(seqList_t));
- }
+ if (_vm->_heroImage != HERO)
+ _vm->_object->swapImages(HERO, _vm->_heroImage);
+
+ _vm->_object->restoreObjects(in);
- in->read(&_vm._heroImage, sizeof(_vm._heroImage));
+ _vm->_heroImage = in->readByte();
// If hero swapped in saved game, swap it
- int heroImg = _vm._heroImage;
+ byte heroImg = _vm->_heroImage;
if (heroImg != HERO)
- _vm.scheduler().swapImages(HERO, _vm._heroImage);
- _vm._heroImage = heroImg;
+ _vm->_object->swapImages(HERO, _vm->_heroImage);
+ _vm->_heroImage = heroImg;
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
- int score;
- in->read(&score, sizeof(score));
- _vm.setScore(score);
+ int score = in->readSint16BE();
+ _vm->setScore(score);
- in->read(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
- in->read(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
- in->read(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
- in->read(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+ gameStatus.storyModeFl = (in->readByte() == 1);
+ gameStatus.jumpExitFl = (in->readByte() == 1);
+ gameStatus.gameOverFl = (in->readByte() == 1);
+ for (int i = 0; i < _vm->_numScreens; i++)
+ _vm->_screenStates[i] = in->readByte();
// Restore points table
- in->read(_vm._points, sizeof(point_t) * _vm._numBonuses);
+ for (int i = 0; i < _vm->_numBonuses; i++) {
+ _vm->_points[i].score = in->readByte();
+ _vm->_points[i].scoredFl = (in->readByte() == 1);
+ }
- // Restore ptrs to currently loaded objects
- for (int i = 0; i < _vm._numObj; i++)
- restoreSeq(&_vm._objects[i]);
+ _vm->_object->restoreAllSeq();
// Now restore time of the save and the event queue
- _vm.scheduler().restoreEvents(in);
+ _vm->_scheduler->restoreEvents(in);
// Restore palette and change it if necessary
- _vm.screen().restorePal(in);
+ _vm->_screen->restorePal(in);
// Restore maze status
- in->read(&_maze, sizeof(maze_t));
-
- delete in;
-}
-
-void FileManager::initSavedGame() {
-// Initialize the size of a saved game (from the fixed initial game).
-// If status.initsave is TRUE, or the initial saved game is not found,
-// force a save to create one. Normally the game will be shipped with
-// the initial game file but useful to force a write during development
-// when the size is changeable.
-// The net result is a valid INITFILE, with status.savesize initialized.
- debugC(1, kDebugFile, "initSavedGame");
-
- // Force save of initial game
- if (_vm.getGameStatus().initSaveFl)
- saveGame(-1, "");
-
- // If initial game doesn't exist, create it
- Common::SeekableReadStream *in = _vm.getSaveFileManager()->openForLoading(_vm._initFilename);
- if (!in) {
- saveGame(-1, "");
- in = _vm.getSaveFileManager()->openForLoading(_vm._initFilename);
- if (!in) {
- Utils::Error(WRITE_ERR, "%s", _vm._initFilename.c_str());
- return;
- }
- }
+ _maze.enabledFl = (in->readByte() == 1);
+ _maze.size = in->readByte();
+ _maze.x1 = in->readSint16BE();
+ _maze.y1 = in->readSint16BE();
+ _maze.x2 = in->readSint16BE();
+ _maze.y2 = in->readSint16BE();
+ _maze.x3 = in->readSint16BE();
+ _maze.x4 = in->readSint16BE();
+ _maze.firstScreenIndex = in->readByte();
- // Must have an open saved game now
- _vm.getGameStatus().saveSize = in->size();
delete in;
-
- // Check sanity - maybe disk full or path set to read-only drive?
- if (_vm.getGameStatus().saveSize == -1)
- Utils::Error(WRITE_ERR, "%s", _vm._initFilename.c_str());
-}
-
-void FileManager::openPlaybackFile(bool playbackFl, bool recordFl) {
- debugC(1, kDebugFile, "openPlaybackFile(%d, %d)", (playbackFl) ? 1 : 0, (recordFl) ? 1 : 0);
-
- if (playbackFl) {
- if (!(fpb = fopen(PBFILE, "r+b")))
- Utils::Error(FILE_ERR, "%s", PBFILE);
- } else if (recordFl) {
- fpb = fopen(PBFILE, "wb");
- }
- pbdata.time = 0; // Say no key available
-}
-
-void FileManager::closePlaybackFile() {
- fclose(fpb);
}
+/**
+* Read the encrypted text from the boot file and print it
+*/
void FileManager::printBootText() {
-// Read the encrypted text from the boot file and print it
debugC(1, kDebugFile, "printBootText");
Common::File ofp;
if (!ofp.open(BOOTFILE)) {
- if (_vm._gameVariant == 3) {
+ if (_vm->_gameVariant == 3) {
//TODO initialize properly _boot structure
warning("printBootText - Skipping as H1 Dos may be a freeware");
return;
} else {
- Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ error("Missing startup file");
}
}
@@ -534,7 +471,7 @@ void FileManager::printBootText() {
// Skip over the boot structure (already read) and read exit text
ofp.seek((long)sizeof(_boot), SEEK_SET);
if (ofp.read(buf, _boot.exit_len) != (size_t)_boot.exit_len)
- Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ error("Error while reading startup file");
// Decrypt the exit text, using CRYPT substring
int i;
@@ -542,33 +479,33 @@ void FileManager::printBootText() {
buf[i] ^= CRYPT[i % strlen(CRYPT)];
buf[i] = '\0';
- //Box(BOX_OK, "%s", buf_p);
- //MessageBox(hwnd, buf_p, "License", MB_ICONINFORMATION);
- warning("printBootText(): License: %s", buf);
+ Utils::Box(BOX_OK, "%s", buf);
}
free(buf);
ofp.close();
}
+/**
+* Reads boot file for program environment. Fatal error if not there or
+* file checksum is bad. De-crypts structure while checking checksum
+*/
void FileManager::readBootFile() {
-// Reads boot file for program environment. Fatal error if not there or
-// file checksum is bad. De-crypts structure while checking checksum
debugC(1, kDebugFile, "readBootFile");
Common::File ofp;
if (!ofp.open(BOOTFILE)) {
- if (_vm._gameVariant == 3) {
+ if (_vm->_gameVariant == 3) {
//TODO initialize properly _boot structure
warning("readBootFile - Skipping as H1 Dos may be a freeware");
return;
} else {
- Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ error("Missing startup file");
}
}
if (ofp.size() < (int32)sizeof(_boot))
- Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ error("Corrupted startup file");
_boot.checksum = ofp.readByte();
_boot.registered = ofp.readByte();
@@ -586,11 +523,13 @@ void FileManager::readBootFile() {
ofp.close();
if (checksum)
- Utils::Error(GEN_ERR, "%s", "Program startup file invalid");
+ error("Corrupted startup file");
}
+/**
+* Returns address of uif_hdr[id], reading it in if first call
+*/
uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
-// Returns address of uif_hdr[id], reading it in if first call
debugC(1, kDebugFile, "getUIFHeader(%d)", id);
static bool firstFl = true;
@@ -602,10 +541,10 @@ uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
// Open unbuffered to do far read
Common::File ip; // Image data file
if (!ip.open(UIF_FILE))
- Utils::Error(FILE_ERR, "%s", UIF_FILE);
+ error("File not found: %s", UIF_FILE);
if (ip.size() < (int32)sizeof(UIFHeader))
- Utils::Error(FILE_ERR, "%s", UIF_FILE);
+ error("Wrong file format: %s", UIF_FILE);
for (int i = 0; i < MAX_UIFS; ++i) {
UIFHeader[i].size = ip.readUint16LE();
@@ -617,38 +556,42 @@ uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
return &UIFHeader[id];
}
+/**
+* Read uif item into supplied buffer.
+*/
void FileManager::readUIFItem(int16 id, byte *buf) {
-// Read uif item into supplied buffer.
debugC(1, kDebugFile, "readUIFItem(%d, ...)", id);
// Open uif file to read data
Common::File ip; // UIF_FILE handle
if (!ip.open(UIF_FILE))
- Utils::Error(FILE_ERR, "%s", UIF_FILE);
+ error("File not found: %s", UIF_FILE);
// Seek to data
uif_hdr_t *UIFHeaderPtr = getUIFHeader((uif_t)id);
ip.seek(UIFHeaderPtr->offset, SEEK_SET);
// We support pcx images and straight data
- seq_t dummySeq; // Dummy seq_t for image data
+ seq_t *dummySeq; // Dummy seq_t for image data
switch (id) {
case UIF_IMAGES: // Read uif images file
- readPCX(ip, &dummySeq, buf, true, UIF_FILE);
+ dummySeq = readPCX(ip, 0, buf, true, UIF_FILE);
+ free(dummySeq);
break;
default: // Read file data into supplied array
if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size)
- Utils::Error(FILE_ERR, "%s", UIF_FILE);
+ error("Wrong file format: %s", UIF_FILE);
break;
}
ip.close();
}
+/**
+* Simple instructions given when F1 pressed twice in a row
+* Only in DOS versions
+*/
void FileManager::instructions() {
-// Simple instructions given when F1 pressed twice in a row
-// Only in DOS versions
-
Common::File f;
if (!f.open(HELPFILE)) {
warning("help.dat not found");
@@ -672,5 +615,14 @@ void FileManager::instructions() {
f.close();
}
+/**
+* Read the uif image file (inventory icons)
+*/
+void FileManager::readUIFImages() {
+ debugC(1, kDebugFile, "readUIFImages");
+
+ readUIFItem(UIF_IMAGES, _vm->_screen->getGUIBuffer()); // Read all uif images
+}
+
} // End of namespace Hugo
diff --git a/engines/hugo/file.h b/engines/hugo/file.h
index a5679d0e1b..1f231d0604 100644
--- a/engines/hugo/file.h
+++ b/engines/hugo/file.h
@@ -41,39 +41,30 @@ struct PCC_header_t { // Structure of PCX file hea
byte mfctr, vers, enc, bpx;
uint16 x1, y1, x2, y2; // bounding box
uint16 xres, yres;
- byte palette[48]; // EGA color palette
+ byte palette[3 * NUM_COLORS]; // EGA color palette
byte vmode, planes;
uint16 bytesPerLine; // Bytes per line
byte fill2[60];
}; // Header of a PCC file
-// Record and playback handling stuff:
-struct pbdata_t {
-// int key; // Character
- uint32 time; // Time at which character was pressed
-};
-
namespace Hugo {
class FileManager {
public:
- FileManager(HugoEngine &vm);
+ FileManager(HugoEngine *vm);
virtual ~FileManager();
bool fileExists(char *filename);
- sound_pt getSound(short sound, uint16 *size);
+ sound_pt getSound(int16 sound, uint16 *size);
- void closePlaybackFile();
- void initSavedGame();
void instructions();
void readBootFile();
void readImage(int objNum, object_t *objPtr);
- void readUIFItem(short id, byte *buf);
- void restoreGame(short slot);
- void restoreSeq(object_t *obj);
- void saveGame(short slot, const char *descrip);
- void saveSeq(object_t *obj);
+ void readUIFImages();
+ void readUIFItem(int16 id, byte *buf);
+ void restoreGame(int16 slot);
+ void saveGame(int16 slot, const char *descrip);
virtual void openDatabaseFiles() = 0;
virtual void closeDatabaseFiles() = 0;
@@ -84,7 +75,7 @@ public:
virtual char *fetchString(int index) = 0;
protected:
- HugoEngine &_vm;
+ HugoEngine *_vm;
Common::File _stringArchive; // Handle for string file
Common::File _sceneryArchive1; // Handle for scenery file
@@ -96,19 +87,13 @@ private:
byte *convertPCC(byte *p, uint16 y, uint16 bpl, image_pt data_p);
uif_hdr_t *getUIFHeader(uif_t id);
- pbdata_t pbdata;
- FILE *fpb;
-
//Strangerke : Not used?
- void openPlaybackFile(bool playbackFl, bool recordFl);
void printBootText();
-// bool pkkey();
-// char pbget();
};
class FileManager_v1d : public FileManager {
public:
- FileManager_v1d(HugoEngine &vm);
+ FileManager_v1d(HugoEngine *vm);
~FileManager_v1d();
void openDatabaseFiles();
@@ -120,7 +105,7 @@ public:
class FileManager_v2d : public FileManager {
public:
- FileManager_v2d(HugoEngine &vm);
+ FileManager_v2d(HugoEngine *vm);
~FileManager_v2d();
void openDatabaseFiles();
@@ -132,7 +117,7 @@ public:
class FileManager_v3d : public FileManager_v2d {
public:
- FileManager_v3d(HugoEngine &vm);
+ FileManager_v3d(HugoEngine *vm);
~FileManager_v3d();
void openDatabaseFiles();
@@ -145,7 +130,7 @@ private:
class FileManager_v1w : public FileManager_v2d {
public:
- FileManager_v1w(HugoEngine &vm);
+ FileManager_v1w(HugoEngine *vm);
~FileManager_v1w();
void readOverlay(int screenNum, image_pt image, ovl_t overlayType);
diff --git a/engines/hugo/file_v1d.cpp b/engines/hugo/file_v1d.cpp
index b6239aa5dc..b92474ea67 100644
--- a/engines/hugo/file_v1d.cpp
+++ b/engines/hugo/file_v1d.cpp
@@ -38,7 +38,7 @@
#include "hugo/util.h"
namespace Hugo {
-FileManager_v1d::FileManager_v1d(HugoEngine &vm) : FileManager(vm) {
+FileManager_v1d::FileManager_v1d(HugoEngine *vm) : FileManager(vm) {
}
FileManager_v1d::~FileManager_v1d() {
@@ -52,49 +52,56 @@ void FileManager_v1d::closeDatabaseFiles() {
debugC(1, kDebugFile, "closeDatabaseFiles");
}
+/**
+* Open and read in an overlay file, close file
+*/
void FileManager_v1d::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
const char *ovl_ext[] = {".b", ".o", ".ob"};
char *buf = (char *) malloc(2048 + 1); // Buffer for file access
- strcat(strcpy(buf, _vm._screenNames[screenNum]), ovl_ext[overlayType]);
+ strcat(strcpy(buf, _vm->_screenNames[screenNum]), ovl_ext[overlayType]);
if (!fileExists(buf)) {
for (uint32 i = 0; i < OVL_SIZE; i++)
image[i] = 0;
+ warning("File not found: %s", buf);
return;
}
if (!_sceneryArchive1.open(buf))
- Utils::Error(FILE_ERR, "%s", buf);
+ error("File not found: %s", buf);
image_pt tmpImage = image; // temp ptr to overlay file
_sceneryArchive1.read(tmpImage, OVL_SIZE);
_sceneryArchive1.close();
+ free(buf);
}
+/**
+* Read a PCX image into dib_a
+*/
void FileManager_v1d::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
char *buf = (char *) malloc(2048 + 1); // Buffer for file access
- strcat(strcpy(buf, _vm._screenNames[screenIndex]), ".ART");
+ strcat(strcpy(buf, _vm->_screenNames[screenIndex]), ".ART");
if (!_sceneryArchive1.open(buf))
- Utils::Error(FILE_ERR, "%s", buf);
+ error("File not found: %s", buf);
// Read the image into dummy seq and static dib_a
- seq_t dummySeq; // Image sequence structure for Read_pcx
- readPCX(_sceneryArchive1, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
-
+ seq_t *dummySeq; // Image sequence structure for Read_pcx
+ dummySeq = readPCX(_sceneryArchive1, 0, _vm->_screen->getFrontBuffer(), true, _vm->_screenNames[screenIndex]);
+ free(dummySeq);
_sceneryArchive1.close();
+ free(buf);
}
char *FileManager_v1d::fetchString(int index) {
debugC(1, kDebugFile, "fetchString(%d)", index);
- return _vm._stringtData[index];
+ return _vm->_stringtData[index];
}
} // End of namespace Hugo
diff --git a/engines/hugo/file_v1w.cpp b/engines/hugo/file_v1w.cpp
index 6ab21a853e..e4f97a917d 100644
--- a/engines/hugo/file_v1w.cpp
+++ b/engines/hugo/file_v1w.cpp
@@ -37,14 +37,16 @@
#include "hugo/util.h"
namespace Hugo {
-FileManager_v1w::FileManager_v1w(HugoEngine &vm) : FileManager_v2d(vm) {
+FileManager_v1w::FileManager_v1w(HugoEngine *vm) : FileManager_v2d(vm) {
}
FileManager_v1w::~FileManager_v1w() {
}
+/**
+* Open and read in an overlay file, close file
+*/
void FileManager_v1w::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
image_pt tmpImage = image; // temp ptr to overlay file
@@ -75,7 +77,7 @@ void FileManager_v1w::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
i = sceneBlock.ob_len;
break;
default:
- Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ error("Bad overlayType: %d", overlayType);
break;
}
if (i == 0) {
diff --git a/engines/hugo/file_v2d.cpp b/engines/hugo/file_v2d.cpp
index 43de3fac4c..baf7f4c97f 100644
--- a/engines/hugo/file_v2d.cpp
+++ b/engines/hugo/file_v2d.cpp
@@ -40,23 +40,29 @@
#include "hugo/util.h"
namespace Hugo {
-FileManager_v2d::FileManager_v2d(HugoEngine &vm) : FileManager(vm) {
+FileManager_v2d::FileManager_v2d(HugoEngine *vm) : FileManager(vm) {
}
FileManager_v2d::~FileManager_v2d() {
}
+/**
+* Open "database" file (packed files)
+*/
void FileManager_v2d::openDatabaseFiles() {
debugC(1, kDebugFile, "openDatabaseFiles");
if (!_stringArchive.open(STRING_FILE))
- Utils::Error(FILE_ERR, "%s", STRING_FILE);
+ error("File not found: %s", STRING_FILE);
if (!_sceneryArchive1.open("scenery.dat"))
- Utils::Error(FILE_ERR, "%s", "scenery.dat");
+ error("File not found: scenery.dat");
if (!_objectsArchive.open(OBJECTS_FILE))
- Utils::Error(FILE_ERR, "%s", OBJECTS_FILE);
+ error("File not found: %s", OBJECTS_FILE);
}
+/**
+* Close "Database" files
+*/
void FileManager_v2d::closeDatabaseFiles() {
debugC(1, kDebugFile, "closeDatabaseFiles");
@@ -65,8 +71,10 @@ void FileManager_v2d::closeDatabaseFiles() {
_objectsArchive.close();
}
+/**
+* Read a PCX image into dib_a
+*/
void FileManager_v2d::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
_sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
@@ -84,15 +92,18 @@ void FileManager_v2d::readBackground(int screenIndex) {
_sceneryArchive1.seek(sceneBlock.scene_off, SEEK_SET);
// Read the image into dummy seq and static dib_a
- seq_t dummySeq; // Image sequence structure for Read_pcx
- readPCX(_sceneryArchive1, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+ seq_t *dummySeq; // Image sequence structure for Read_pcx
+ dummySeq = readPCX(_sceneryArchive1, 0, _vm->_screen->getFrontBuffer(), true, _vm->_screenNames[screenIndex]);
+ free(dummySeq);
}
+/**
+* Open and read in an overlay file, close file
+*/
void FileManager_v2d::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
- image_pt tmpImage = image; // temp ptr to overlay file
+ image_pt tmpImage = image; // temp ptr to overlay file
_sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
sceneBlock_t sceneBlock; // Database header entry
@@ -120,7 +131,7 @@ void FileManager_v2d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
i = sceneBlock.ob_len;
break;
default:
- Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ error("Bad overlayType: %d", overlayType);
break;
}
if (i == 0) {
@@ -147,30 +158,32 @@ void FileManager_v2d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
} while (k < OVL_SIZE);
}
+/**
+* Fetch string from file, decode and return ptr to string in memory
+*/
char *FileManager_v2d::fetchString(int index) {
-// Fetch string from file, decode and return ptr to string in memory
debugC(1, kDebugFile, "fetchString(%d)", index);
// Get offset to string[index] (and next for length calculation)
_stringArchive.seek((uint32)index * sizeof(uint32), SEEK_SET);
uint32 off1, off2;
if (_stringArchive.read((char *)&off1, sizeof(uint32)) == 0)
- Utils::Error(FILE_ERR, "%s", "String offset");
+ error("An error has occurred: bad String offset");
if (_stringArchive.read((char *)&off2, sizeof(uint32)) == 0)
- Utils::Error(FILE_ERR, "%s", "String offset");
+ error("An error has occurred: bad String offset");
// Check size of string
if ((off2 - off1) >= MAX_BOX)
- Utils::Error(FILE_ERR, "%s", "Fetched string too long!");
+ error("Fetched string too long!");
// Position to string and read it into gen purpose _textBoxBuffer
_stringArchive.seek(off1, SEEK_SET);
if (_stringArchive.read(_textBoxBuffer, (uint16)(off2 - off1)) == 0)
- Utils::Error(FILE_ERR, "%s", "Fetch_string");
+ error("An error has occurred: fetchString");
// Null terminate, decode and return it
_textBoxBuffer[off2-off1] = '\0';
- _vm.scheduler().decodeString(_textBoxBuffer);
+ _vm->_scheduler->decodeString(_textBoxBuffer);
return _textBoxBuffer;
}
} // End of namespace Hugo
diff --git a/engines/hugo/file_v3d.cpp b/engines/hugo/file_v3d.cpp
index e4809a7208..e98b4cc013 100644
--- a/engines/hugo/file_v3d.cpp
+++ b/engines/hugo/file_v3d.cpp
@@ -39,18 +39,20 @@
#include "hugo/util.h"
namespace Hugo {
-FileManager_v3d::FileManager_v3d(HugoEngine &vm) : FileManager_v2d(vm) {
+FileManager_v3d::FileManager_v3d(HugoEngine *vm) : FileManager_v2d(vm) {
}
FileManager_v3d::~FileManager_v3d() {
}
+/**
+* Read a PCX image into dib_a
+*/
void FileManager_v3d::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
_sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
-
+
sceneBlock_t sceneBlock; // Read a database header entry
sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
@@ -61,31 +63,38 @@ void FileManager_v3d::readBackground(int screenIndex) {
sceneBlock.ob_off = _sceneryArchive1.readUint32LE();
sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
- seq_t dummySeq; // Image sequence structure for Read_pcx
+ seq_t *dummySeq; // Image sequence structure for Read_pcx
if (screenIndex < 20) {
_sceneryArchive1.seek(sceneBlock.scene_off, SEEK_SET);
// Read the image into dummy seq and static dib_a
- readPCX(_sceneryArchive1, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+ dummySeq = readPCX(_sceneryArchive1, 0, _vm->_screen->getFrontBuffer(), true, _vm->_screenNames[screenIndex]);
} else {
_sceneryArchive2.seek(sceneBlock.scene_off, SEEK_SET);
// Read the image into dummy seq and static dib_a
- readPCX(_sceneryArchive2, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+ dummySeq = readPCX(_sceneryArchive2, 0, _vm->_screen->getFrontBuffer(), true, _vm->_screenNames[screenIndex]);
}
+ free(dummySeq);
}
+/**
+* Open "database" file (packed files)
+*/
void FileManager_v3d::openDatabaseFiles() {
debugC(1, kDebugFile, "openDatabaseFiles");
if (!_stringArchive.open(STRING_FILE))
- Utils::Error(FILE_ERR, "%s", STRING_FILE);
+ error("File not found: %s", STRING_FILE);
if (!_sceneryArchive1.open("scenery1.dat"))
- Utils::Error(FILE_ERR, "%s", "scenery1.dat");
+ error("File not found: scenery1.dat");
if (!_sceneryArchive2.open("scenery2.dat"))
- Utils::Error(FILE_ERR, "%s", "scenery2.dat");
+ error("File not found: scenery2.dat");
if (!_objectsArchive.open(OBJECTS_FILE))
- Utils::Error(FILE_ERR, "%s", OBJECTS_FILE);
+ error("File not found: %s", OBJECTS_FILE);
}
+/**
+* Close "Database" files
+*/
void FileManager_v3d::closeDatabaseFiles() {
debugC(1, kDebugFile, "closeDatabaseFiles");
@@ -95,13 +104,15 @@ void FileManager_v3d::closeDatabaseFiles() {
_objectsArchive.close();
}
+/**
+* Open and read in an overlay file, close file
+*/
void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
image_pt tmpImage = image; // temp ptr to overlay file
_sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
-
+
sceneBlock_t sceneBlock; // Database header entry
sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
@@ -113,7 +124,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
uint32 i = 0;
-
+
if (screenNum < 20) {
switch (overlayType) {
case BOUNDARY:
@@ -129,7 +140,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
i = sceneBlock.ob_len;
break;
default:
- Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ error("Bad overlayType: %d", overlayType);
break;
}
if (i == 0) {
@@ -137,7 +148,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
image[i] = 0;
return;
}
-
+
// Read in the overlay file using MAC Packbits. (We're not proud!)
int16 k = 0; // byte count
do {
@@ -149,7 +160,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
*tmpImage++ = _sceneryArchive1.readByte();
} else { // Repeat next byte -data+1 times
int16 j = _sceneryArchive1.readByte();
-
+
for (i = 0; i < (byte)(-data + 1); i++, k++)
*tmpImage++ = j;
}
@@ -169,7 +180,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
i = sceneBlock.ob_len;
break;
default:
- Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ error("Bad overlayType: %d", overlayType);
break;
}
if (i == 0) {
@@ -177,7 +188,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
image[i] = 0;
return;
}
-
+
// Read in the overlay file using MAC Packbits. (We're not proud!)
int16 k = 0; // byte count
do {
@@ -189,7 +200,7 @@ void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayTy
*tmpImage++ = _sceneryArchive2.readByte();
} else { // Repeat next byte -data+1 times
int16 j = _sceneryArchive2.readByte();
-
+
for (i = 0; i < (byte)(-data + 1); i++, k++)
*tmpImage++ = j;
}
diff --git a/engines/hugo/game.h b/engines/hugo/game.h
index 869aa1baa4..01e3788ad0 100644
--- a/engines/hugo/game.h
+++ b/engines/hugo/game.h
@@ -106,6 +106,7 @@ namespace Hugo {
// Macros:
#define TPS ((_config.turboFl) ? TURBO_TPS : NORMAL_TPS)
+#define NUM_COLORS 16 // Num colors to save in palette
#define MAX_UIFS 32 // Max possible uif items in hdr
#define NUM_FONTS 3 // Number of dib fonts
#define FIRST_FONT U_FONT5
@@ -123,28 +124,34 @@ enum TEXTCOLORS {
enum uif_t {U_FONT5, U_FONT6, U_FONT8, UIF_IMAGES, NUM_UIF_ITEMS};
-// Enumerate overlay file types
+/**
+* Enumerate overlay file types
+*/
enum ovl_t {BOUNDARY, OVERLAY, OVLBASE};
-// Enumerate error types
-enum {
- GEN_ERR, FILE_ERR, WRITE_ERR, PCCH_ERR, HEAP_ERR, EVNT_ERR, SOUND_ERR
- //MOUSE_ERR, VID_ERR, FONT_ERR, ARG_ERR, CHK_ERR, TIMER_ERR, VBX_ERR
-};
-
-// Enumerate ways of cycling a sequence of frames
+/**
+* Enumerate ways of cycling a sequence of frames
+*/
enum cycle_t {INVISIBLE, ALMOST_INVISIBLE, NOT_CYCLING, CYCLE_FORWARD, CYCLE_BACKWARD};
-// Enumerate sequence index matching direction of travel
+/**
+* Enumerate sequence index matching direction of travel
+*/
enum {RIGHT, LEFT, DOWN, _UP};
-// Channel requirement during sound effect
+/**
+* Channel requirement during sound effect
+*/
enum stereo_t {BOTH_CHANNELS, RIGHT_CHANNEL, LEFT_CHANNEL};
-// Priority for sound effect
+/**
+* Priority for sound effect
+*/
enum priority_t {LOW_PRI, MED_PRI, HIGH_PRI};
-// Enumerate the different path types for an object
+/**
+* Enumerate the different path types for an object
+*/
enum path_t {
USER, // User has control of object via cursor keys
AUTO, // Computer has control, controlled by action lists
@@ -155,42 +162,57 @@ enum path_t {
WANDER2 // Same as WANDER, except keeps cycling when stationary
};
-// Enumerate whether object is foreground, background or 'floating'
-// If floating, HERO can collide with it and fore/back ground is determined
-// by relative y-coord of object base. This is the general case.
-// If fore or background, no collisions can take place and object is either
-// behind or in front of all others, although can still be hidden by the
-// the overlay plane. OVEROVL means the object is FLOATING (to other
-// objects) but is never hidden by the overlay plane
+/**
+* Enumerate whether object is foreground, background or 'floating'
+* If floating, HERO can collide with it and fore/back ground is determined
+* by relative y-coord of object base. This is the general case.
+* If fore or background, no collisions can take place and object is either
+* behind or in front of all others, although can still be hidden by the
+* the overlay plane. OVEROVL means the object is FLOATING (to other
+* objects) but is never hidden by the overlay plane
+*/
enum {FOREGROUND, BACKGROUND, FLOATING, OVEROVL};
-// Game view state machine
+/**
+* Game view state machine
+*/
enum vstate_t {V_IDLE, V_INTROINIT, V_INTRO, V_PLAY, V_INVENT, V_EXIT};
enum font_t {LARGE_ROMAN, MED_ROMAN, NUM_GDI_FONTS, INIT_FONTS, DEL_FONTS};
-// Ways to dismiss a text/prompt box
+/**
+* Ways to dismiss a text/prompt box
+*/
enum box_t {BOX_ANY, BOX_OK, BOX_PROMPT, BOX_YESNO};
-// Standard viewport sizes
-enum wsize_t {SIZE_DEF, SIZE_1, SIZE_2, SIZE_3};
-
-// Display list functions
+/**
+* Display list functions
+*/
enum dupdate_t {D_INIT, D_ADD, D_DISPLAY, D_RESTORE};
-// General device installation commands
+/**
+* General device installation commands
+*/
enum inst_t {INSTALL, RESTORE, RESET};
-// Inventory icon bar states
+/**
+* Inventory icon bar states
+*/
enum istate_t {I_OFF, I_UP, I_DOWN, I_ACTIVE};
-// Actions for Process_inventory()
+/**
+* Actions for Process_inventory()
+*/
enum invact_t {INV_INIT, INV_LEFT, INV_RIGHT, INV_GET};
-// Purpose of an automatic route
+/**
+* Purpose of an automatic route
+*/
enum go_t {GO_SPACE, GO_EXIT, GO_LOOK, GO_GET};
-// Following defines the action types and action list
+/**
+* Following defines the action types and action list
+*/
enum action_t { // Parameters:
ANULL = 0xff, // Special NOP used to 'delete' events in DEL_EVENTS
ASCHEDULE = 0, // 0 - Ptr to action list to be rescheduled
@@ -259,17 +281,23 @@ struct uif_hdr_t { // UIF font/image look up
uint32 offset; // Offset of item in file
};
-// Game specific type definitions
+/**
+* Game specific type definitions
+*/
typedef byte *image_pt; // ptr to an object image (sprite)
typedef byte *sound_pt; // ptr to sound (or music) data
-// Following are points for achieving certain actions.
+/**
+* Following are points for achieving certain actions.
+*/
struct point_t {
byte score; // The value of the point
bool scoredFl; // Whether scored yet
};
-// Structure for initializing maze processing
+/**
+* Structure for initializing maze processing
+*/
struct maze_t {
bool enabledFl; // TRUE when maze processing enabled
byte size; // Size of (square) maze matrix
@@ -684,7 +712,9 @@ union act {
act49 a49;
};
-// The following determines how a verb is acted on, for an object
+/**
+* The following determines how a verb is acted on, for an object
+*/
struct cmd {
uint16 verbIndex; // the verb
uint16 reqIndex; // ptr to list of required objects
@@ -696,8 +726,10 @@ struct cmd {
uint16 actIndex; // Ptr to action list if verb done
};
-// The following is a linked list of images in an animation sequence
-// The image data is in 8-bit DIB format, i.e. 1 byte = 1 pixel
+/**
+* The following is a linked list of images in an animation sequence
+* The image data is in 8-bit DIB format, i.e. 1 byte = 1 pixel
+*/
struct seq_t { // Linked list of images
byte *imagePtr; // ptr to image
uint16 bytesPerLine8; // bytes per line (8bits)
@@ -706,13 +738,17 @@ struct seq_t { // Linked list of images
seq_t *nextSeqPtr; // ptr to next record
};
-// The following is an array of structures of above sequences
+/**
+* The following is an array of structures of above sequences
+*/
struct seqList_t {
uint16 imageNbr; // Number of images in sequence
seq_t *seqPtr; // Ptr to sequence structure
};
-// Following is definition of object attributes
+/**
+* Following is definition of object attributes
+*/
struct object_t {
uint16 nounIndex; // String identifying object
uint16 dataIndex; // String describing the object
@@ -747,10 +783,12 @@ struct object_t {
int8 oldvy; // Previous vy
};
-// Following is structure of verbs and nouns for 'background' objects
-// These are objects that appear in the various screens, but nothing
-// interesting ever happens with them. Rather than just be dumb and say
-// "don't understand" we produce an interesting msg to keep user sane.
+/**
+* Following is structure of verbs and nouns for 'background' objects
+* These are objects that appear in the various screens, but nothing
+* interesting ever happens with them. Rather than just be dumb and say
+* "don't understand" we produce an interesting msg to keep user sane.
+*/
struct background_t {
uint16 verbIndex;
uint16 nounIndex;
@@ -769,7 +807,9 @@ typedef byte icondib_t[XPIX *INV_DY]; // Icon bar dib
typedef char command_t[MAX_CHARS + 8]; // Command line (+spare for prompt,cursor)
typedef char fpath_t[MAX_FPATH]; // File path
-// Structure to define an EXIT or other collision-activated hotspot
+/**
+* Structure to define an EXIT or other collision-activated hotspot
+*/
struct hotspot_t {
int screenIndex; // Screen in which hotspot appears
int x1, y1, x2, y2; // Bounding box of hotspot
@@ -778,16 +818,10 @@ struct hotspot_t {
};
struct status_t { // Game status (not saved)
- bool initSaveFl; // Force save of initial game
bool storyModeFl; // Game is telling story - no commands
bool gameOverFl; // Game is over - hero knobbled
- bool playbackFl; // Game is in playback mode
- bool recordFl; // Game is in record mode
bool demoFl; // Game is in demo mode
- bool debugFl; // Game is in debug mode
bool textBoxFl; // Game is (halted) in text box
-// Strangerke - Not used ?
-// bool mmtimeFl; // Multimedia timer supported
bool lookFl; // Toolbar "look" button pressed
bool recallFl; // Toolbar "recall" button pressed
bool leftButtonFl; // Left mouse button pressed
@@ -798,7 +832,6 @@ struct status_t { // Game status (not saved)
bool helpFl; // Calling WinHelp (don't disable music)
bool doQuitFl;
uint32 tick; // Current time in ticks
- uint32 saveTick; // Time of last save in ticks
vstate_t viewState; // View state machine
istate_t inventoryState; // Inventory icon bar state
int16 inventoryHeight; // Inventory icon bar height
@@ -807,11 +840,17 @@ struct status_t { // Game status (not saved)
go_t go_for; // Purpose of an automatic route
int16 go_id; // Index of exit of object walking to
fpath_t path; // Alternate path for saved files
- long saveSize; // Size of a saved game
int16 saveSlot; // Current slot to save/restore game
- int16 screenWidth; // Desktop screen width
int16 song; // Current song
int16 cx, cy; // Cursor position (dib coords)
+
+// Strangerke - Suppress as related to playback
+// bool playbackFl; // Game is in playback mode
+// bool recordFl; // Game is in record mode
+// Strangerke - Not used ?
+// bool mmtimeFl; // Multimedia timer supported
+// int16 screenWidth; // Desktop screen width
+// uint32 saveTick; // Time of last save in ticks
};
struct config_t { // User's config (saved)
@@ -842,7 +881,9 @@ extern hugo_boot_t _boot; // Boot info structure
extern char _textBoxBuffer[]; // Useful box text buffer
extern command_t _line; // Line of user text input
-// Structure of scenery file lookup entry
+/**
+* Structure of scenery file lookup entry
+*/
struct sceneBlock_t {
uint32 scene_off;
uint32 scene_len;
@@ -854,12 +895,6 @@ struct sceneBlock_t {
uint32 ob_len;
};
-// Structure of object file lookup entry
-struct objBlock_t {
- uint32 objOffset;
- uint32 objLength;
-};
-
#include "common/pack-start.h" // START STRUCT PACKING
struct sound_hdr_t { // Sound file lookup entry
uint16 size; // Size of sound data in bytes
diff --git a/engines/hugo/global.h b/engines/hugo/global.h
index 0ab21ddb9c..43a1e39b8f 100644
--- a/engines/hugo/global.h
+++ b/engines/hugo/global.h
@@ -38,7 +38,6 @@ namespace Hugo {
#define MAX_SOUNDS 64 // Max number of sounds
#define BOOTFILE "HUGO.BSF" // Name of boot structure file
#define LEN_MASK 0x3F // Lower 6 bits are length
-#define PBFILE "playback.dat"
// Name scenery and objects picture databases
#define OBJECTS_FILE "objects.dat"
diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp
index cdc74b5ae5..06cd7db62b 100644
--- a/engines/hugo/hugo.cpp
+++ b/engines/hugo/hugo.cpp
@@ -24,7 +24,9 @@
*/
#include "common/system.h"
+#include "common/random.h"
#include "common/events.h"
+#include "common/EventRecorder.h"
#include "common/debug-channels.h"
#include "hugo/hugo.h"
@@ -37,8 +39,10 @@
#include "hugo/inventory.h"
#include "hugo/parser.h"
#include "hugo/route.h"
+#include "hugo/util.h"
#include "hugo/sound.h"
#include "hugo/intro.h"
+#include "hugo/object.h"
#include "engines/util.h"
@@ -51,12 +55,18 @@ overlay_t HugoEngine::_overlay;
overlay_t HugoEngine::_ovlBase;
overlay_t HugoEngine::_objBound;
+config_t _config; // User's config
+maze_t _maze; // Default to not in maze
+hugo_boot_t _boot; // Boot info structure file
+char _textBoxBuffer[MAX_BOX]; // Buffer for text box
+command_t _line; // Line of user text input
+
HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(syst), _gameDescription(gd), _mouseX(0), _mouseY(0),
- _textData(0), _stringtData(0), _screenNames(0), _textEngine(0), _textIntro(0), _textMouse(0), _textParser(0), _textSchedule(0), _textUtil(0),
+ _textData(0), _stringtData(0), _screenNames(0), _textEngine(0), _textIntro(0), _textMouse(0), _textParser(0), _textUtil(0),
_arrayNouns(0), _arrayVerbs(0), _arrayReqs(0), _hotspots(0), _invent(0), _uses(0), _catchallList(0), _backgroundObjects(0),
- _points(0), _cmdList(0), _screenActs(0), _objects(0), _actListArr(0), _heroImage(0), _defltTunes(0), _palette(0), _introX(0),
- _introY(0), _maxInvent(0), _numBonuses(0), _numScreens(0), _tunesNbr(0), _soundSilence(0), _soundTest(0), _screenStates(0), _numObj(0),
- _score(0), _maxscore(0)
+ _points(0), _cmdList(0), _screenActs(0), _heroImage(0), _defltTunes(0), _introX(0), _introY(0), _maxInvent(0), _numBonuses(0),
+ _numScreens(0), _tunesNbr(0), _soundSilence(0), _soundTest(0), _screenStates(0), _score(0), _maxscore(0),
+ _backgroundObjectsSize(0), _screenActsSize(0), _usesSize(0)
{
DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
@@ -67,73 +77,95 @@ HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(sy
DebugMan.addDebugChannel(kDebugFile, "File", "File IO debug level");
DebugMan.addDebugChannel(kDebugRoute, "Route", "Route debug level");
DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Inventory debug level");
+ DebugMan.addDebugChannel(kDebugObject, "Object", "Object debug level");
- for (int j = 0; j < NUM_FONTS; j++)
- _arrayFont[j] = 0;
+ _console = new HugoConsole(this);
}
HugoEngine::~HugoEngine() {
- delete _soundHandler;
- delete _route;
- delete _parser;
- delete _inventoryHandler;
- delete _mouseHandler;
- delete _screen;
- delete _scheduler;
- delete _fileManager;
-
- free(_palette);
- free(_introX);
- free(_introY);
-
-#if 0
freeTexts(_textData);
freeTexts(_stringtData);
+
+ if (_arrayNouns) {
+ for (int i = 0; _arrayNouns[i]; i++)
+ freeTexts(_arrayNouns[i]);
+ free(_arrayNouns);
+ }
+
+ if (_arrayVerbs) {
+ for (int i = 0; _arrayVerbs[i]; i++)
+ freeTexts(_arrayVerbs[i]);
+ free(_arrayVerbs);
+ }
+
+ freeTexts(_screenNames);
+ _screen->freePalette();
freeTexts(_textEngine);
freeTexts(_textIntro);
+ free(_introX);
+ free(_introY);
freeTexts(_textMouse);
freeTexts(_textParser);
- freeTexts(_textSchedule);
freeTexts(_textUtil);
-#endif
- free(_textData);
- free(_stringtData);
- free(_screenNames);
- free(_textEngine);
- free(_textIntro);
- free(_textMouse);
- free(_textParser);
- free(_textSchedule);
- free(_textUtil);
-
- warning("Missing: free _arrayNouns");
- warning("Missing: free _arrayVerbs");
-
- free(_arrayReqs);
+
+ if (_arrayReqs) {
+ for (int i = 0; _arrayReqs[i] != 0; i++)
+ free(_arrayReqs[i]);
+ free(_arrayReqs);
+ }
+
free(_hotspots);
free(_invent);
- free(_uses);
+
+ if (_uses) {
+ for (int i = 0; i < _usesSize; i++)
+ free(_uses[i].targets);
+ free(_uses);
+ }
+
free(_catchallList);
- warning("Missing: free _background_objects");
+ if (_backgroundObjects) {
+ for (int i = 0; i < _backgroundObjectsSize; i++)
+ free(_backgroundObjects[i]);
+ free(_backgroundObjects);
+ }
free(_points);
- warning("Missing: free _cmdList");
- warning("Missing: free _screenActs");
- warning("Missing: free _objects");
+ if (_cmdList) {
+ for (int i = 0; i < _cmdListSize; i++)
+ free(_cmdList[i]);
+ free(_cmdList);
+ }
+
+ if (_cmdList) {
+ for (int i = 0; i < _screenActsSize; i++)
+ free(_screenActs[i]);
+ free(_screenActs);
+ }
+
+ _object->freeObjectArr();
+ _scheduler->freeActListArr();
free(_defltTunes);
free(_screenStates);
- if (_arrayFont[0])
- free(_arrayFont[0]);
+ _screen->freeFonts();
- if (_arrayFont[1])
- free(_arrayFont[1]);
+ delete _object;
+ delete _sound;
+ delete _route;
+ delete _parser;
+ delete _inventory;
+ delete _mouse;
+ delete _screen;
+ delete _intro;
+ delete _scheduler;
+ delete _file;
- if (_arrayFont[2])
- free(_arrayFont[2]);
+ DebugMan.clearAllDebugChannels();
+ delete _console;
}
GameType HugoEngine::getGameType() const {
@@ -152,53 +184,59 @@ Common::Error HugoEngine::run() {
s_Engine = this;
initGraphics(320, 200, false);
- _mouseHandler = new MouseHandler(*this);
- _inventoryHandler = new InventoryHandler(*this);
- _route = new Route(*this);
- _soundHandler = new SoundHandler(*this);
+ _mouse = new MouseHandler(this);
+ _inventory = new InventoryHandler(this);
+ _route = new Route(this);
+ _sound = new SoundHandler(this);
switch (_gameVariant) {
case 0: // H1 Win
- _fileManager = new FileManager_v1w(*this);
- _scheduler = new Scheduler_v3d(*this);
- _introHandler = new intro_v1w(*this);
- _screen = new Screen_v1w(*this);
- _parser = new Parser_v1w(*this);
+ _file = new FileManager_v1w(this);
+ _scheduler = new Scheduler_v1w(this);
+ _intro = new intro_v1w(this);
+ _screen = new Screen_v1w(this);
+ _parser = new Parser_v1w(this);
+ _object = new ObjectHandler_v1w(this);
break;
case 1:
- _fileManager = new FileManager_v2d(*this);
- _scheduler = new Scheduler_v3d(*this);
- _introHandler = new intro_v2w(*this);
- _screen = new Screen_v1w(*this);
- _parser = new Parser_v1w(*this);
+ _file = new FileManager_v2d(this);
+ _scheduler = new Scheduler_v1w(this);
+ _intro = new intro_v2w(this);
+ _screen = new Screen_v1w(this);
+ _parser = new Parser_v1w(this);
+ _object = new ObjectHandler_v1w(this);
break;
case 2:
- _fileManager = new FileManager_v2d(*this);
- _scheduler = new Scheduler_v3d(*this);
- _introHandler = new intro_v3w(*this);
- _screen = new Screen_v1w(*this);
- _parser = new Parser_v1w(*this);
+ _file = new FileManager_v2d(this);
+ _scheduler = new Scheduler_v1w(this);
+ _intro = new intro_v3w(this);
+ _screen = new Screen_v1w(this);
+ _parser = new Parser_v1w(this);
+ _object = new ObjectHandler_v1w(this);
break;
case 3: // H1 DOS
- _fileManager = new FileManager_v1d(*this);
- _scheduler = new Scheduler_v1d(*this);
- _introHandler = new intro_v1d(*this);
- _screen = new Screen_v1d(*this);
- _parser = new Parser_v1d(*this);
+ _file = new FileManager_v1d(this);
+ _scheduler = new Scheduler_v1d(this);
+ _intro = new intro_v1d(this);
+ _screen = new Screen_v1d(this);
+ _parser = new Parser_v1d(this);
+ _object = new ObjectHandler_v1d(this);
break;
case 4:
- _fileManager = new FileManager_v2d(*this);
- _scheduler = new Scheduler_v1d(*this);
- _introHandler = new intro_v2d(*this);
- _screen = new Screen_v1d(*this);
- _parser = new Parser_v2d(*this);
+ _file = new FileManager_v2d(this);
+ _scheduler = new Scheduler_v2d(this);
+ _intro = new intro_v2d(this);
+ _screen = new Screen_v1d(this);
+ _parser = new Parser_v2d(this);
+ _object = new ObjectHandler_v2d(this);
break;
case 5:
- _fileManager = new FileManager_v3d(*this);
- _scheduler = new Scheduler_v3d(*this);
- _introHandler = new intro_v3d(*this);
- _screen = new Screen_v1d(*this);
- _parser = new Parser_v3d(*this);
+ _file = new FileManager_v3d(this);
+ _scheduler = new Scheduler_v3d(this);
+ _intro = new intro_v3d(this);
+ _screen = new Screen_v1d(this);
+ _parser = new Parser_v3d(this);
+ _object = new ObjectHandler_v1d(this);
break;
}
@@ -216,8 +254,6 @@ Common::Error HugoEngine::run() {
initialize();
initConfig(RESET); // Reset user's config
- file().restoreGame(-1);
-
initMachine();
// Start the state machine
@@ -234,7 +270,11 @@ Common::Error HugoEngine::run() {
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
- parser().keyHandler(event.kbd.keycode, 0);
+ if (event.kbd.keycode == Common::KEYCODE_d && event.kbd.hasFlags(Common::KBD_CTRL)) {
+ this->getDebugger()->attach();
+ this->getDebugger()->onFrame();
+ }
+ _parser->keyHandler(event.kbd.keycode, 0);
break;
case Common::EVENT_MOUSEMOVE:
_mouseX = event.mouse.x;
@@ -259,6 +299,7 @@ Common::Error HugoEngine::run() {
break;
}
}
+ _status.doQuitFl |= shouldQuit(); // update game quit flag
}
return Common::kNoError;
}
@@ -267,14 +308,16 @@ void HugoEngine::initMachine() {
if (_gameVariant == kGameVariantH1Dos)
readScreenFiles(0);
else
- file().readBackground(_numScreens - 1); // Splash screen
- readObjectImages(); // Read all object images
+ _file->readBackground(_numScreens - 1); // Splash screen
+ _object->readObjectImages(); // Read all object images
if (_platform == Common::kPlatformWindows)
- readUIFImages(); // Read all uif images (only in Win versions)
+ _file->readUIFImages(); // Read all uif images (only in Win versions)
}
+/**
+* Hugo game state machine - called during onIdle
+*/
void HugoEngine::runMachine() {
-// Hugo game state machine - called during onIdle
static uint32 lastTime;
status_t &gameStatus = getGameStatus();
@@ -293,32 +336,32 @@ void HugoEngine::runMachine() {
switch (gameStatus.viewState) {
case V_IDLE: // Not processing state machine
- intro().preNewGame(); // Any processing before New Game selected
+ _intro->preNewGame(); // Any processing before New Game selected
break;
case V_INTROINIT: // Initialization before intro begins
- intro().introInit();
+ _intro->introInit();
g_system->showMouse(false);
gameStatus.viewState = V_INTRO;
break;
case V_INTRO: // Do any game-dependant preamble
- if (intro().introPlay()) { // Process intro screen
- scheduler().newScreen(0); // Initialize first screen
+ if (_intro->introPlay()) { // Process intro screen
+ _scheduler->newScreen(0); // Initialize first screen
gameStatus.viewState = V_PLAY;
}
break;
case V_PLAY: // Playing game
g_system->showMouse(true);
- parser().charHandler(); // Process user cmd input
- moveObjects(); // Process object movement
- scheduler().runScheduler(); // Process any actions
- screen().displayList(D_RESTORE); // Restore previous background
- updateImages(); // Draw into _frontBuffer, compile display list
- mouse().mouseHandler(); // Mouse activity - adds to display list
- screen().drawStatusText();
- screen().displayList(D_DISPLAY); // Blit the display list to screen
+ _parser->charHandler(); // Process user cmd input
+ _object->moveObjects(); // Process object movement
+ _scheduler->runScheduler(); // Process any actions
+ _screen->displayList(D_RESTORE); // Restore previous background
+ _object->updateImages(); // Draw into _frontBuffer, compile display list
+ _mouse->mouseHandler(); // Mouse activity - adds to display list
+ _screen->drawStatusText();
+ _screen->displayList(D_DISPLAY); // Blit the display list to screen
break;
case V_INVENT: // Accessing inventory
- inventory().runInventory(); // Process Inventory state machine
+ _inventory->runInventory(); // Process Inventory state machine
break;
case V_EXIT: // Game over or user exited
gameStatus.viewState = V_IDLE;
@@ -327,6 +370,9 @@ void HugoEngine::runMachine() {
}
}
+/**
+* Loads Hugo.dat file, which contains all the hardcoded data in the original executables
+*/
bool HugoEngine::loadHugoDat() {
Common::File in;
in.open("hugo.dat");
@@ -379,11 +425,7 @@ bool HugoEngine::loadHugoDat() {
// Read screenNames
_screenNames = loadTextsVariante(in, &_numScreens);
- // Read palette
- _paletteSize = in.readUint16BE();
- _palette = (byte *)malloc(sizeof(byte) * _paletteSize);
- for (int i = 0; i < _paletteSize; i++)
- _palette[i] = in.readByte();
+ _screen->loadPalette(in);
// Read textEngine
_textEngine = loadTexts(in);
@@ -416,11 +458,8 @@ bool HugoEngine::loadHugoDat() {
// Read textParser
_textParser = loadTexts(in);
- // Read textSchedule
- _textSchedule = loadTexts(in);
-
// Read textUtil
- _textUtil = loadTexts(in);
+ _textUtil = loadTextsVariante(in, 0);
// Read _arrayReqs
_arrayReqs = loadLongArray(in);
@@ -456,7 +495,7 @@ bool HugoEngine::loadHugoDat() {
}
}
- int numElem, numSubElem, numSubAct;
+ int numElem, numSubElem;
//Read _invent
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
@@ -475,6 +514,7 @@ bool HugoEngine::loadHugoDat() {
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
if (varnt == _gameVariant) {
+ _usesSize = numElem;
_uses = (uses_t *)malloc(sizeof(uses_t) * numElem);
for (int i = 0; i < numElem; i++) {
_uses[i].objId = in.readSint16BE();
@@ -528,8 +568,9 @@ bool HugoEngine::loadHugoDat() {
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
if (varnt == _gameVariant) {
- _backgroundObjects = (background_t **)malloc(sizeof(background_t *) * numElem);
- for (int i = 0; i < numElem; i++) {
+ _backgroundObjectsSize = numElem;
+ _backgroundObjects = (background_t **)malloc(sizeof(background_t *) * _backgroundObjectsSize);
+ for (int i = 0; i < _backgroundObjectsSize; i++) {
numSubElem = in.readUint16BE();
_backgroundObjects[i] = (background_t *)malloc(sizeof(background_t) * numSubElem);
for (int j = 0; j < numSubElem; j++) {
@@ -576,8 +617,9 @@ bool HugoEngine::loadHugoDat() {
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
if (varnt == _gameVariant) {
- _cmdList = (cmd **)malloc(sizeof(cmd *) * numElem);
- for (int i = 0; i < numElem; i++) {
+ _cmdListSize = numElem;
+ _cmdList = (cmd **)malloc(sizeof(cmd *) * _cmdListSize);
+ for (int i = 0; i < _cmdListSize; i++) {
numSubElem = in.readUint16BE();
_cmdList[i] = (cmd *)malloc(sizeof(cmd) * numSubElem);
for (int j = 0; j < numSubElem; j++) {
@@ -608,13 +650,14 @@ bool HugoEngine::loadHugoDat() {
}
}
-// TODO: For Hugo2 and Hugo3, if not in story mode, increment _screenActs[0][0] (ex: kALcrashStory + 1 == kALcrashNoStory)
// Read _screenActs
+ // TODO: For Hugo2 and Hugo3, if not in story mode, increment _screenActs[0][0] (ex: kALcrashStory + 1 == kALcrashNoStory)
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
if (varnt == _gameVariant) {
- _screenActs = (uint16 **)malloc(sizeof(uint16 *) * numElem);
- for (int i = 0; i < numElem; i++) {
+ _screenActsSize = numElem;
+ _screenActs = (uint16 **)malloc(sizeof(uint16 *) * _screenActsSize);
+ for (int i = 0; i < _screenActsSize; i++) {
numSubElem = in.readUint16BE();
if (numSubElem == 0) {
_screenActs[i] = 0;
@@ -633,682 +676,23 @@ bool HugoEngine::loadHugoDat() {
}
}
-// TODO: For Hugo3, if not in story mode, set _objects[2].state to 3
- for (int varnt = 0; varnt < _numVariant; varnt++) {
- numElem = in.readUint16BE();
- if (varnt == _gameVariant) {
- _objects = (object_t *)malloc(sizeof(object_t) * numElem);
- for (int i = 0; i < numElem; i++) {
- _objects[i].nounIndex = in.readUint16BE();
- _objects[i].dataIndex = in.readUint16BE();
- numSubElem = in.readUint16BE();
- if (numSubElem == 0)
- _objects[i].stateDataIndex = 0;
- else
- _objects[i].stateDataIndex = (uint16 *)malloc(sizeof(uint16) * numSubElem);
- for (int j = 0; j < numSubElem; j++)
- _objects[i].stateDataIndex[j] = in.readUint16BE();
- _objects[i].pathType = (path_t) in.readSint16BE();
- _objects[i].vxPath = in.readSint16BE();
- _objects[i].vyPath = in.readSint16BE();
- _objects[i].actIndex = in.readUint16BE();
- _objects[i].seqNumb = in.readByte();
- _objects[i].currImagePtr = 0;
- if (_objects[i].seqNumb == 0) {
- _objects[i].seqList[0].imageNbr = 0;
- _objects[i].seqList[0].seqPtr = 0;
- }
- for (int j = 0; j < _objects[i].seqNumb; j++) {
- _objects[i].seqList[j].imageNbr = in.readUint16BE();
- _objects[i].seqList[j].seqPtr = 0;
- }
- _objects[i].cycling = (cycle_t)in.readByte();
- _objects[i].cycleNumb = in.readByte();
- _objects[i].frameInterval = in.readByte();
- _objects[i].frameTimer = in.readByte();
- _objects[i].radius = in.readByte();
- _objects[i].screenIndex = in.readByte();
- _objects[i].x = in.readSint16BE();
- _objects[i].y = in.readSint16BE();
- _objects[i].oldx = in.readSint16BE();
- _objects[i].oldy = in.readSint16BE();
- _objects[i].vx = in.readByte();
- _objects[i].vy = in.readByte();
- _objects[i].objValue = in.readByte();
- _objects[i].genericCmd = in.readSint16BE();
- _objects[i].cmdIndex = in.readUint16BE();
- _objects[i].carriedFl = (in.readByte() != 0);
- _objects[i].state = in.readByte();
- _objects[i].verbOnlyFl = (in.readByte() != 0);
- _objects[i].priority = in.readByte();
- _objects[i].viewx = in.readSint16BE();
- _objects[i].viewy = in.readSint16BE();
- _objects[i].direction = in.readSint16BE();
- _objects[i].curSeqNum = in.readByte();
- _objects[i].curImageNum = in.readByte();
- _objects[i].oldvx = in.readByte();
- _objects[i].oldvy = in.readByte();
- }
- } else {
- for (int i = 0; i < numElem; i++) {
- in.readUint16BE();
- in.readUint16BE();
- numSubElem = in.readUint16BE();
- for (int j = 0; j < numSubElem; j++)
- in.readUint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- numSubElem = in.readByte();
- for (int j = 0; j < numSubElem; j++)
- in.readUint16BE();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readSint16BE();
- in.readUint16BE();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readByte();
- in.readByte();
- in.readByte();
- in.readByte();
- }
- }
- }
-//#define HERO 0
- _hero = &_objects[HERO]; // This always points to hero
- _screen_p = &(_objects[HERO].screenIndex); // Current screen is hero's
- _heroImage = HERO; // Current in use hero image
+ _object->loadObjectArr(in);
+
+ _hero = &_object->_objects[HERO]; // This always points to hero
+ _screen_p = &(_object->_objects[HERO].screenIndex); // Current screen is hero's
+ _heroImage = HERO; // Current in use hero image
+
+ _scheduler->loadActListArr(in);
-//read _actListArr
- for (int varnt = 0; varnt < _numVariant; varnt++) {
- numElem = in.readUint16BE();
- if (varnt == _gameVariant) {
- _actListArr = (act **)malloc(sizeof(act *) * numElem);
- for (int i = 0; i < numElem; i++) {
- numSubElem = in.readUint16BE();
- _actListArr[i] = (act *) malloc(sizeof(act) * (numSubElem + 1));
- for (int j = 0; j < numSubElem; j++) {
- _actListArr[i][j].a0.actType = (action_t) in.readByte();
- switch (_actListArr[i][j].a0.actType) {
- case ANULL: // -1
- break;
- case ASCHEDULE: // 0
- _actListArr[i][j].a0.timer = in.readSint16BE();
- _actListArr[i][j].a0.actIndex = in.readUint16BE();
- break;
- case START_OBJ: // 1
- _actListArr[i][j].a1.timer = in.readSint16BE();
- _actListArr[i][j].a1.objNumb = in.readSint16BE();
- _actListArr[i][j].a1.cycleNumb = in.readSint16BE();
- _actListArr[i][j].a1.cycle = (cycle_t) in.readByte();
- break;
- case INIT_OBJXY: // 2
- _actListArr[i][j].a2.timer = in.readSint16BE();
- _actListArr[i][j].a2.objNumb = in.readSint16BE();
- _actListArr[i][j].a2.x = in.readSint16BE();
- _actListArr[i][j].a2.y = in.readSint16BE();
- break;
- case PROMPT: // 3
- _actListArr[i][j].a3.timer = in.readSint16BE();
- _actListArr[i][j].a3.promptIndex = in.readSint16BE();
- numSubAct = in.readUint16BE();
- _actListArr[i][j].a3.responsePtr = (int *) malloc(sizeof(int) * numSubAct);
- for (int k = 0; k < numSubAct; k++)
- _actListArr[i][j].a3.responsePtr[k] = in.readSint16BE();
- _actListArr[i][j].a3.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a3.actFailIndex = in.readUint16BE();
- _actListArr[i][j].a3.encodedFl = (in.readByte() == 1) ? true : false;
- break;
- case BKGD_COLOR: // 4
- _actListArr[i][j].a4.timer = in.readSint16BE();
- _actListArr[i][j].a4.newBackgroundColor = in.readUint32BE();
- break;
- case INIT_OBJVXY: // 5
- _actListArr[i][j].a5.timer = in.readSint16BE();
- _actListArr[i][j].a5.objNumb = in.readSint16BE();
- _actListArr[i][j].a5.vx = in.readSint16BE();
- _actListArr[i][j].a5.vy = in.readSint16BE();
- break;
- case INIT_CARRY: // 6
- _actListArr[i][j].a6.timer = in.readSint16BE();
- _actListArr[i][j].a6.objNumb = in.readSint16BE();
- _actListArr[i][j].a6.carriedFl = (in.readByte() == 1) ? true : false;
- break;
- case INIT_HF_COORD: // 7
- _actListArr[i][j].a7.timer = in.readSint16BE();
- _actListArr[i][j].a7.objNumb = in.readSint16BE();
- break;
- case NEW_SCREEN: // 8
- _actListArr[i][j].a8.timer = in.readSint16BE();
- _actListArr[i][j].a8.screenIndex = in.readSint16BE();
- break;
- case INIT_OBJSTATE: // 9
- _actListArr[i][j].a9.timer = in.readSint16BE();
- _actListArr[i][j].a9.objNumb = in.readSint16BE();
- _actListArr[i][j].a9.newState = in.readByte();
- break;
- case INIT_PATH: // 10
- _actListArr[i][j].a10.timer = in.readSint16BE();
- _actListArr[i][j].a10.objNumb = in.readSint16BE();
- _actListArr[i][j].a10.newPathType = in.readSint16BE();
- _actListArr[i][j].a10.vxPath = in.readByte();
- _actListArr[i][j].a10.vyPath = in.readByte();
- break;
- case COND_R: // 11
- _actListArr[i][j].a11.timer = in.readSint16BE();
- _actListArr[i][j].a11.objNumb = in.readSint16BE();
- _actListArr[i][j].a11.stateReq = in.readByte();
- _actListArr[i][j].a11.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a11.actFailIndex = in.readUint16BE();
- break;
- case TEXT: // 12
- _actListArr[i][j].a12.timer = in.readSint16BE();
- _actListArr[i][j].a12.stringIndex = in.readSint16BE();
- break;
- case SWAP_IMAGES: // 13
- _actListArr[i][j].a13.timer = in.readSint16BE();
- _actListArr[i][j].a13.obj1 = in.readSint16BE();
- _actListArr[i][j].a13.obj2 = in.readSint16BE();
- break;
- case COND_SCR: // 14
- _actListArr[i][j].a14.timer = in.readSint16BE();
- _actListArr[i][j].a14.objNumb = in.readSint16BE();
- _actListArr[i][j].a14.screenReq = in.readSint16BE();
- _actListArr[i][j].a14.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a14.actFailIndex = in.readUint16BE();
- break;
- case AUTOPILOT: // 15
- _actListArr[i][j].a15.timer = in.readSint16BE();
- _actListArr[i][j].a15.obj1 = in.readSint16BE();
- _actListArr[i][j].a15.obj2 = in.readSint16BE();
- _actListArr[i][j].a15.dx = in.readByte();
- _actListArr[i][j].a15.dy = in.readByte();
- break;
- case INIT_OBJ_SEQ: // 16
- _actListArr[i][j].a16.timer = in.readSint16BE();
- _actListArr[i][j].a16.objNumb = in.readSint16BE();
- _actListArr[i][j].a16.seqIndex = in.readSint16BE();
- break;
- case SET_STATE_BITS: // 17
- _actListArr[i][j].a17.timer = in.readSint16BE();
- _actListArr[i][j].a17.objNumb = in.readSint16BE();
- _actListArr[i][j].a17.stateMask = in.readSint16BE();
- break;
- case CLEAR_STATE_BITS: // 18
- _actListArr[i][j].a18.timer = in.readSint16BE();
- _actListArr[i][j].a18.objNumb = in.readSint16BE();
- _actListArr[i][j].a18.stateMask = in.readSint16BE();
- break;
- case TEST_STATE_BITS: // 19
- _actListArr[i][j].a19.timer = in.readSint16BE();
- _actListArr[i][j].a19.objNumb = in.readSint16BE();
- _actListArr[i][j].a19.stateMask = in.readSint16BE();
- _actListArr[i][j].a19.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a19.actFailIndex = in.readUint16BE();
- break;
- case DEL_EVENTS: // 20
- _actListArr[i][j].a20.timer = in.readSint16BE();
- _actListArr[i][j].a20.actTypeDel = (action_t) in.readByte();
- break;
- case GAMEOVER: // 21
- _actListArr[i][j].a21.timer = in.readSint16BE();
- break;
- case INIT_HH_COORD: // 22
- _actListArr[i][j].a22.timer = in.readSint16BE();
- _actListArr[i][j].a22.objNumb = in.readSint16BE();
- break;
- case EXIT: // 23
- _actListArr[i][j].a23.timer = in.readSint16BE();
- break;
- case BONUS: // 24
- _actListArr[i][j].a24.timer = in.readSint16BE();
- _actListArr[i][j].a24.pointIndex = in.readSint16BE();
- break;
- case COND_BOX: // 25
- _actListArr[i][j].a25.timer = in.readSint16BE();
- _actListArr[i][j].a25.objNumb = in.readSint16BE();
- _actListArr[i][j].a25.x1 = in.readSint16BE();
- _actListArr[i][j].a25.y1 = in.readSint16BE();
- _actListArr[i][j].a25.x2 = in.readSint16BE();
- _actListArr[i][j].a25.y2 = in.readSint16BE();
- _actListArr[i][j].a25.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a25.actFailIndex = in.readUint16BE();
- break;
- case SOUND: // 26
- _actListArr[i][j].a26.timer = in.readSint16BE();
- _actListArr[i][j].a26.soundIndex = in.readSint16BE();
- break;
- case ADD_SCORE: // 27
- _actListArr[i][j].a27.timer = in.readSint16BE();
- _actListArr[i][j].a27.objNumb = in.readSint16BE();
- break;
- case SUB_SCORE: // 28
- _actListArr[i][j].a28.timer = in.readSint16BE();
- _actListArr[i][j].a28.objNumb = in.readSint16BE();
- break;
- case COND_CARRY: // 29
- _actListArr[i][j].a29.timer = in.readSint16BE();
- _actListArr[i][j].a29.objNumb = in.readSint16BE();
- _actListArr[i][j].a29.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a29.actFailIndex = in.readUint16BE();
- break;
- case INIT_MAZE: // 30
- _actListArr[i][j].a30.timer = in.readSint16BE();
- _actListArr[i][j].a30.mazeSize = in.readByte();
- _actListArr[i][j].a30.x1 = in.readSint16BE();
- _actListArr[i][j].a30.y1 = in.readSint16BE();
- _actListArr[i][j].a30.x2 = in.readSint16BE();
- _actListArr[i][j].a30.y2 = in.readSint16BE();
- _actListArr[i][j].a30.x3 = in.readSint16BE();
- _actListArr[i][j].a30.x4 = in.readSint16BE();
- _actListArr[i][j].a30.firstScreenIndex = in.readByte();
- break;
- case EXIT_MAZE: // 31
- _actListArr[i][j].a31.timer = in.readSint16BE();
- break;
- case INIT_PRIORITY: // 32
- _actListArr[i][j].a32.timer = in.readSint16BE();
- _actListArr[i][j].a32.objNumb = in.readSint16BE();
- _actListArr[i][j].a32.priority = in.readByte();
- break;
- case INIT_SCREEN: // 33
- _actListArr[i][j].a33.timer = in.readSint16BE();
- _actListArr[i][j].a33.objNumb = in.readSint16BE();
- _actListArr[i][j].a33.screenIndex = in.readSint16BE();
- break;
- case AGSCHEDULE: // 34
- _actListArr[i][j].a34.timer = in.readSint16BE();
- _actListArr[i][j].a34.actIndex = in.readUint16BE();
- break;
- case REMAPPAL: // 35
- _actListArr[i][j].a35.timer = in.readSint16BE();
- _actListArr[i][j].a35.oldColorIndex = in.readSint16BE();
- _actListArr[i][j].a35.newColorIndex = in.readSint16BE();
- break;
- case COND_NOUN: // 36
- _actListArr[i][j].a36.timer = in.readSint16BE();
- _actListArr[i][j].a36.nounIndex = in.readUint16BE();
- _actListArr[i][j].a36.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a36.actFailIndex = in.readUint16BE();
- break;
- case SCREEN_STATE: // 37
- _actListArr[i][j].a37.timer = in.readSint16BE();
- _actListArr[i][j].a37.screenIndex = in.readSint16BE();
- _actListArr[i][j].a37.newState = in.readByte();
- break;
- case INIT_LIPS: // 38
- _actListArr[i][j].a38.timer = in.readSint16BE();
- _actListArr[i][j].a38.lipsObjNumb = in.readSint16BE();
- _actListArr[i][j].a38.objNumb = in.readSint16BE();
- _actListArr[i][j].a38.dxLips = in.readByte();
- _actListArr[i][j].a38.dyLips = in.readByte();
- break;
- case INIT_STORY_MODE: // 39
- _actListArr[i][j].a39.timer = in.readSint16BE();
- _actListArr[i][j].a39.storyModeFl = (in.readByte() == 1);
- break;
- case WARN: // 40
- _actListArr[i][j].a40.timer = in.readSint16BE();
- _actListArr[i][j].a40.stringIndex = in.readSint16BE();
- break;
- case COND_BONUS: // 41
- _actListArr[i][j].a41.timer = in.readSint16BE();
- _actListArr[i][j].a41.BonusIndex = in.readSint16BE();
- _actListArr[i][j].a41.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a41.actFailIndex = in.readUint16BE();
- break;
- case TEXT_TAKE: // 42
- _actListArr[i][j].a42.timer = in.readSint16BE();
- _actListArr[i][j].a42.objNumb = in.readSint16BE();
- break;
- case YESNO: // 43
- _actListArr[i][j].a43.timer = in.readSint16BE();
- _actListArr[i][j].a43.promptIndex = in.readSint16BE();
- _actListArr[i][j].a43.actYesIndex = in.readUint16BE();
- _actListArr[i][j].a43.actNoIndex = in.readUint16BE();
- break;
- case STOP_ROUTE: // 44
- _actListArr[i][j].a44.timer = in.readSint16BE();
- break;
- case COND_ROUTE: // 45
- _actListArr[i][j].a45.timer = in.readSint16BE();
- _actListArr[i][j].a45.routeIndex = in.readSint16BE();
- _actListArr[i][j].a45.actPassIndex = in.readUint16BE();
- _actListArr[i][j].a45.actFailIndex = in.readUint16BE();
- break;
- case INIT_JUMPEXIT: // 46
- _actListArr[i][j].a46.timer = in.readSint16BE();
- _actListArr[i][j].a46.jumpExitFl = (in.readByte() == 1);
- break;
- case INIT_VIEW: // 47
- _actListArr[i][j].a47.timer = in.readSint16BE();
- _actListArr[i][j].a47.objNumb = in.readSint16BE();
- _actListArr[i][j].a47.viewx = in.readSint16BE();
- _actListArr[i][j].a47.viewy = in.readSint16BE();
- _actListArr[i][j].a47.direction = in.readSint16BE();
- break;
- case INIT_OBJ_FRAME: // 48
- _actListArr[i][j].a48.timer = in.readSint16BE();
- _actListArr[i][j].a48.objNumb = in.readSint16BE();
- _actListArr[i][j].a48.seqIndex = in.readSint16BE();
- _actListArr[i][j].a48.frameIndex = in.readSint16BE();
- break;
- case OLD_SONG: //49
- _actListArr[i][j].a49.timer = in.readSint16BE();
- _actListArr[i][j].a49.soundIndex = in.readUint16BE();
- break;
- default:
- error("Engine - Unknown action type encountered: %d", _actListArr[i][j].a0.actType);
- }
- }
- _actListArr[i][numSubElem].a0.actType = ANULL;
- }
- } else {
- for (int i = 0; i < numElem; i++) {
- numSubElem = in.readUint16BE();
- for (int j = 0; j < numSubElem; j++) {
- numSubAct = in.readByte();
- switch (numSubAct) {
- case ANULL: // -1
- break;
- case ASCHEDULE: // 0
- in.readSint16BE();
- in.readUint16BE();
- break;
- case START_OBJ: // 1
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- break;
- case INIT_OBJXY: // 2
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case PROMPT: // 3
- in.readSint16BE();
- in.readSint16BE();
- numSubAct = in.readUint16BE();
- for (int k = 0; k < numSubAct; k++)
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- in.readByte();
- break;
- case BKGD_COLOR: // 4
- in.readSint16BE();
- in.readUint32BE();
- break;
- case INIT_OBJVXY: // 5
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case INIT_CARRY: // 6
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- break;
- case INIT_HF_COORD: // 7
- in.readSint16BE();
- in.readSint16BE();
- break;
- case NEW_SCREEN: // 8
- in.readSint16BE();
- in.readSint16BE();
- break;
- case INIT_OBJSTATE: // 9
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- break;
- case INIT_PATH: // 10
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- in.readByte();
- break;
- case COND_R: // 11
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case TEXT: // 12
- in.readSint16BE();
- in.readSint16BE();
- break;
- case SWAP_IMAGES: // 13
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case COND_SCR: // 14
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case AUTOPILOT: // 15
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- in.readByte();
- break;
- case INIT_OBJ_SEQ: // 16
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case SET_STATE_BITS: // 17
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case CLEAR_STATE_BITS: // 18
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case TEST_STATE_BITS: // 19
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case DEL_EVENTS: // 20
- in.readSint16BE();
- in.readByte();
- break;
- case GAMEOVER: // 21
- in.readSint16BE();
- break;
- case INIT_HH_COORD: // 22
- in.readSint16BE();
- in.readSint16BE();
- break;
- case EXIT: // 23
- in.readSint16BE();
- break;
- case BONUS: // 24
- in.readSint16BE();
- in.readSint16BE();
- break;
- case COND_BOX: // 25
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case SOUND: // 26
- in.readSint16BE();
- in.readSint16BE();
- break;
- case ADD_SCORE: // 27
- in.readSint16BE();
- in.readSint16BE();
- break;
- case SUB_SCORE: // 28
- in.readSint16BE();
- in.readSint16BE();
- break;
- case COND_CARRY: // 29
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case INIT_MAZE: // 30
- in.readSint16BE();
- in.readByte();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- break;
- case EXIT_MAZE: // 31
- in.readSint16BE();
- break;
- case INIT_PRIORITY: // 32
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- break;
- case INIT_SCREEN: // 33
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case AGSCHEDULE: // 34
- in.readSint16BE();
- in.readUint16BE();
- break;
- case REMAPPAL: // 35
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case COND_NOUN: // 36
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case SCREEN_STATE: // 37
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- break;
- case INIT_LIPS: // 38
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readByte();
- in.readByte();
- break;
- case INIT_STORY_MODE: // 39
- in.readSint16BE();
- in.readByte();
- break;
- case WARN: // 40
- in.readSint16BE();
- in.readSint16BE();
- break;
- case COND_BONUS: // 41
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case TEXT_TAKE: // 42
- in.readSint16BE();
- in.readSint16BE();
- break;
- case YESNO: // 43
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case STOP_ROUTE: // 44
- in.readSint16BE();
- break;
- case COND_ROUTE: // 45
- in.readSint16BE();
- in.readSint16BE();
- in.readUint16BE();
- in.readUint16BE();
- break;
- case INIT_JUMPEXIT: // 46
- in.readSint16BE();
- in.readByte();
- break;
- case INIT_VIEW: // 47
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case INIT_OBJ_FRAME: // 48
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- in.readSint16BE();
- break;
- case OLD_SONG: //49
- in.readSint16BE();
- in.readUint16BE();
- break;
- default:
- error("Engine - Unknown action type encountered %d - variante %d pos %d.%d", numSubAct, varnt, i, j);
- }
- }
- }
- }
- }
for (int varnt = 0; varnt < _numVariant; varnt++) {
if (varnt == _gameVariant) {
- _tunesNbr = in.readByte();
- _soundSilence = in.readByte();
- _soundTest = in.readByte();
+ _tunesNbr = in.readSByte();
+ _soundSilence = in.readSByte();
+ _soundTest = in.readSByte();
} else {
- in.readByte();
- in.readByte();
- in.readByte();
+ in.readSByte();
+ in.readSByte();
+ in.readSByte();
}
}
@@ -1349,48 +733,10 @@ bool HugoEngine::loadHugoDat() {
}
}
- //Read LASTOBJ
- for (int varnt = 0; varnt < _numVariant; varnt++) {
- numElem = in.readUint16BE();
- if (varnt == _gameVariant)
- _numObj = numElem;
- }
+ _object->loadNumObj(in);
+ _scheduler->loadAlNewscrIndex(in);
+ _screen->loadFontArr(in);
- //Read kALnewscr used by maze (Hugo 2)
- for (int varnt = 0; varnt < _numVariant; varnt++) {
- numElem = in.readUint16BE();
- if (varnt == _gameVariant)
- _alNewscrIndex = numElem;
- }
-
- if (_gameVariant > 2) {
- _arrayFontSize[0] = in.readUint16BE();
- _arrayFont[0] = (byte *)malloc(sizeof(byte) * _arrayFontSize[0]);
- for (int j = 0; j < _arrayFontSize[0]; j++)
- _arrayFont[0][j] = in.readByte();
-
- _arrayFontSize[1] = in.readUint16BE();
- _arrayFont[1] = (byte *)malloc(sizeof(byte) * _arrayFontSize[1]);
- for (int j = 0; j < _arrayFontSize[1]; j++)
- _arrayFont[1][j] = in.readByte();
-
- _arrayFontSize[2] = in.readUint16BE();
- _arrayFont[2] = (byte *)malloc(sizeof(byte) * _arrayFontSize[2]);
- for (int j = 0; j < _arrayFontSize[2]; j++)
- _arrayFont[2][j] = in.readByte();
- } else {
- numElem = in.readUint16BE();
- for (int j = 0; j < numElem; j++)
- in.readByte();
-
- numElem = in.readUint16BE();
- for (int j = 0; j < numElem; j++)
- in.readByte();
-
- numElem = in.readUint16BE();
- for (int j = 0; j < numElem; j++)
- in.readByte();
- }
return true;
}
@@ -1400,6 +746,7 @@ char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) {
int len;
char **res = 0;
char *pos = 0;
+ char *posBck = 0;
for (int varnt = 0; varnt < _numVariant; varnt++) {
numTexts = in.readUint16BE();
@@ -1414,6 +761,7 @@ char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) {
res[0] += DATAALIGNMENT;
} else {
in.read(pos, entryLen);
+ posBck = pos;
}
pos += DATAALIGNMENT;
@@ -1427,6 +775,9 @@ char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) {
if (varnt == _gameVariant)
res[i] = pos;
}
+
+ if (varnt != _gameVariant)
+ free(posBck);
}
return res;
@@ -1459,17 +810,19 @@ uint16 **HugoEngine::loadLongArray(Common::File &in) {
char ***HugoEngine::loadTextsArray(Common::File &in) {
char ***resArray = 0;
+ uint16 arraySize;
for (int varnt = 0; varnt < _numVariant; varnt++) {
- int numNouns = in.readUint16BE();
+ arraySize = in.readUint16BE();
if (varnt == _gameVariant) {
- resArray = (char ** *)malloc(sizeof(char **) * (numNouns + 1));
- resArray[numNouns] = 0;
+ resArray = (char ***)malloc(sizeof(char **) * (arraySize + 1));
+ resArray[arraySize] = 0;
}
- for (int i = 0; i < numNouns; i++) {
+ for (int i = 0; i < arraySize; i++) {
int numTexts = in.readUint16BE();
int entryLen = in.readUint16BE();
char *pos = (char *)malloc(entryLen);
+ char *posBck = 0;
char **res = 0;
if (varnt == _gameVariant) {
res = (char **)malloc(sizeof(char *) * numTexts);
@@ -1478,6 +831,7 @@ char ***HugoEngine::loadTextsArray(Common::File &in) {
res[0] += DATAALIGNMENT;
} else {
in.read(pos, entryLen);
+ posBck = pos;
}
pos += DATAALIGNMENT;
@@ -1493,6 +847,8 @@ char ***HugoEngine::loadTextsArray(Common::File &in) {
if (varnt == _gameVariant)
resArray[i] = res;
+ else
+ free(posBck);
}
}
@@ -1524,8 +880,380 @@ void HugoEngine::freeTexts(char **ptr) {
if (!ptr)
return;
- free(*ptr);
+ free(*ptr - DATAALIGNMENT);
free(ptr);
}
+/**
+* Sets the playlist to be the default tune selection
+*/
+void HugoEngine::initPlaylist(bool playlist[MAX_TUNES]) {
+ debugC(1, kDebugEngine, "initPlaylist");
+
+ for (int16 i = 0; i < MAX_TUNES; i++)
+ playlist[i] = false;
+ for (int16 i = 0; _defltTunes[i] != -1; i++)
+ playlist[_defltTunes[i]] = true;
+}
+
+/**
+* Initialize the dynamic game status
+*/
+void HugoEngine::initStatus() {
+ debugC(1, kDebugEngine, "initStatus");
+ _status.storyModeFl = false; // Not in story mode
+ _status.gameOverFl = false; // Hero not knobbled yet
+ _status.demoFl = false; // Not demo mode
+ _status.textBoxFl = false; // Not processing a text box
+ _status.lookFl = false; // Toolbar "look" button
+ _status.recallFl = false; // Toolbar "recall" button
+ _status.leftButtonFl = false; // Left mouse button pressed
+ _status.rightButtonFl = false; // Right mouse button pressed
+ _status.newScreenFl = false; // Screen not just loaded
+ _status.jumpExitFl = false; // Can't jump to a screen exit
+ _status.godModeFl = false; // No special cheats allowed
+ _status.helpFl = false; // Not calling WinHelp()
+ _status.doQuitFl = false;
+ _status.path[0] = 0; // Path to write files
+ _status.saveSlot = 0; // Slot to save/restore game
+
+ // Initialize every start of new game
+ _status.tick = 0; // Tick count
+ _status.viewState = V_IDLE; // View state
+ _status.inventoryState = I_OFF; // Inventory icon bar state
+ _status.inventoryHeight = 0; // Inventory icon bar pos
+ _status.inventoryObjId = -1; // Inventory object selected (none)
+ _status.routeIndex = -1; // Hero not following a route
+ _status.go_for = GO_SPACE; // Hero walking to space
+ _status.go_id = -1; // Hero not walking to anything
+
+// Strangerke - Suppress as related to playback
+// _status.recordFl = false; // Not record mode
+// _status.playbackFl = false; // Not playback mode
+// Strangerke - Not used ?
+// _status.mmtime = false; // Multimedia timer support
+// _status.screenWidth = 0; // Desktop screen width
+// _status.saveTick = 0; // Time of last save
+}
+
+/**
+* Initialize default config values. Must be done before Initialize().
+* Reset needed to save config.cx,cy which get splatted during OnFileNew()
+*/
+void HugoEngine::initConfig(inst_t action) {
+ debugC(1, kDebugEngine, "initConfig(%d)", action);
+
+ switch (action) {
+ case INSTALL:
+ _config.musicFl = true; // Music state initially on
+ _config.soundFl = true; // Sound state initially on
+ _config.turboFl = false; // Turbo state initially off
+ _config.backgroundMusicFl = false; // No music when inactive
+ _config.musicVolume = 85; // Music volume %
+ _config.soundVolume = 100; // Sound volume %
+ initPlaylist(_config.playlist); // Initialize default tune playlist
+
+ _file->readBootFile(); // Read startup structure
+ break;
+ case RESET:
+ // Find first tune and play it
+ for (int16 i = 0; i < MAX_TUNES; i++) {
+ if (_config.playlist[i]) {
+ _sound->playMusic(i);
+ break;
+ }
+ }
+ break;
+ case RESTORE:
+ warning("Unhandled action RESTORE");
+ break;
+ }
+}
+
+void HugoEngine::initialize() {
+ debugC(1, kDebugEngine, "initialize");
+
+ _maze.enabledFl = false;
+ _line[0] = '\0';
+
+ _sound->initSound();
+ _scheduler->initEventQueue(); // Init scheduler stuff
+ _screen->initDisplay(); // Create Dibs and palette
+ _file->openDatabaseFiles(); // Open database files
+ calcMaxScore(); // Initialise maxscore
+
+ _rnd = new Common::RandomSource();
+ g_eventRec.registerRandomSource(*_rnd, "hugo");
+
+ _rnd->setSeed(42); // Kick random number generator
+
+ switch (_gameVariant) {
+ case kGameVariantH1Dos:
+ _episode = "\"Hugo's House of Horrors\"";
+ _picDir = "";
+ break;
+ case kGameVariantH2Dos:
+ _episode = "\"Hugo 2: Whodunit?\"";
+ _picDir = "hugo2/";
+ break;
+ case kGameVariantH3Dos:
+ _episode = "\"Hugo III: Jungle of Doom\"";
+ _picDir = "hugo3/";
+ break;
+ case kGameVariantH1Win:
+ _episode = "\"Hugo's Horrific Adventure\"";
+ _picDir = "";
+ break;
+ case kGameVariantH2Win:
+ _episode = "\"Hugo's Mystery Adventure\"";
+ _picDir = "hugo2/";
+ break;
+ case kGameVariantH3Win:
+ _episode = "\"Hugo's Amazon Adventure\"";
+ _picDir = "hugo3/";
+ break;
+ default:
+ error("Unknown game");
+ }
+}
+
+/**
+* Restore all resources before termination
+*/
+void HugoEngine::shutdown() {
+ debugC(1, kDebugEngine, "shutdown");
+
+ _file->closeDatabaseFiles();
+ _object->freeObjects();
+}
+
+/**
+* Read scenery, overlay files for given screen number
+*/
+void HugoEngine::readScreenFiles(int screenNum) {
+ debugC(1, kDebugEngine, "readScreenFiles(%d)", screenNum);
+
+ _file->readBackground(screenNum); // Scenery file
+ memcpy(_screen->getBackBuffer(), _screen->getFrontBuffer(), sizeof(_screen->getFrontBuffer()));// Make a copy
+ _file->readOverlay(screenNum, _boundary, BOUNDARY); // Boundary file
+ _file->readOverlay(screenNum, _overlay, OVERLAY); // Overlay file
+ _file->readOverlay(screenNum, _ovlBase, OVLBASE); // Overlay base file
+}
+
+/**
+* Return maximum allowed movement (from zero to vx) such that object does
+* not cross a boundary (either background or another object)
+*/
+int HugoEngine::deltaX(int x1, int x2, int vx, int y) {
+// Explanation of algorithm: The boundaries are drawn as contiguous
+// lines 1 pixel wide. Since DX,DY are not necessarily 1, we must
+// detect boundary crossing. If vx positive, examine each pixel from
+// x1 old to x2 new, else x2 old to x1 new, both at the y2 line.
+// If vx zero, no need to check. If vy non-zero then examine each
+// pixel on the line segment x1 to x2 from y old to y new.
+// Fix from Hugo I v1.5:
+// Note the diff is munged in the return statement to cater for a special
+// cases arising from differences in image widths from one sequence to
+// another. The problem occurs reversing direction at a wall where the
+// new image intersects before the object can move away. This is cured
+// by comparing the intersection with half the object width pos. If the
+// intersection is in the other half wrt the intended direction, use the
+// desired vx, else use the computed delta. i.e. believe the desired vx
+
+ debugC(3, kDebugEngine, "deltaX(%d, %d, %d, %d)", x1, x2, vx, y);
+
+ if (vx == 0)
+ return 0 ; // Object stationary
+
+ y *= XBYTES; // Offset into boundary file
+ if (vx > 0) {
+ // Moving to right
+ for (int i = x1 >> 3; i <= (x2 + vx) >> 3; i++) {// Search by byte
+ int b = Utils::firstBit((byte)(_boundary[y + i] | _objBound[y + i]));
+ if (b < 8) { // b is index or 8
+ // Compute x of boundary and test if intersection
+ b += i << 3;
+ if ((b >= x1) && (b <= x2 + vx))
+ return (b < x1 + ((x2 - x1) >> 1)) ? vx : b - x2 - 1; // return dx
+ }
+ }
+ } else {
+ // Moving to left
+ for (int i = x2 >> 3; i >= (x1 + vx) >> 3; i--) {// Search by byte
+ int b = Utils::lastBit((byte)(_boundary[y + i] | _objBound[y + i]));
+ if (b < 8) { // b is index or 8
+ // Compute x of boundary and test if intersection
+ b += i << 3;
+ if ((b >= x1 + vx) && (b <= x2))
+ return (b > x1 + ((x2 - x1) >> 1)) ? vx : b - x1 + 1; // return dx
+ }
+ }
+ }
+ return vx;
+}
+
+/**
+* Similar to Delta_x, but for movement in y direction. Special case of
+* bytes at end of line segment; must only count boundary bits falling on
+* line segment.
+*/
+int HugoEngine::deltaY(int x1, int x2, int vy, int y) {
+ debugC(3, kDebugEngine, "deltaY(%d, %d, %d, %d)", x1, x2, vy, y);
+
+ if (vy == 0)
+ return 0; // Object stationary
+
+ int inc = (vy > 0) ? 1 : -1;
+ for (int j = y + inc; j != (y + vy + inc); j += inc) { //Search by byte
+ for (int i = x1 >> 3; i <= x2 >> 3; i++) {
+ int b = _boundary[j * XBYTES + i] | _objBound[j * XBYTES + i];
+ if (b != 0) { // Any bit set
+ // Make sure boundary bits fall on line segment
+ if (i == (x2 >> 3)) // Adjust right end
+ b &= 0xff << ((i << 3) + 7 - x2);
+ else if (i == (x1 >> 3)) // Adjust left end
+ b &= 0xff >> (x1 - (i << 3));
+ if (b)
+ return j - y - inc;
+ }
+ }
+ }
+ return vy;
+}
+
+/**
+* Store a horizontal line segment in the object boundary file
+*/
+void HugoEngine::storeBoundary(int x1, int x2, int y) {
+ debugC(5, kDebugEngine, "storeBoundary(%d, %d, %d)", x1, x2, y);
+
+ for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
+ byte *b = &_objBound[y * XBYTES + i]; // get boundary byte
+ if (i == x2 >> 3) // Adjust right end
+ *b |= 0xff << ((i << 3) + 7 - x2);
+ else if (i == x1 >> 3) // Adjust left end
+ *b |= 0xff >> (x1 - (i << 3));
+ else
+ *b = 0xff;
+ }
+}
+
+/**
+* Clear a horizontal line segment in the object boundary file
+*/
+void HugoEngine::clearBoundary(int x1, int x2, int y) {
+ debugC(5, kDebugEngine, "clearBoundary(%d, %d, %d)", x1, x2, y);
+
+ for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
+ byte *b = &_objBound[y * XBYTES + i]; // get boundary byte
+ if (i == x2 >> 3) // Adjust right end
+ *b &= ~(0xff << ((i << 3) + 7 - x2));
+ else if (i == x1 >> 3) // Adjust left end
+ *b &= ~(0xff >> (x1 - (i << 3)));
+ else
+ *b = 0;
+ }
+}
+
+/**
+* Search background command list for this screen for supplied object.
+* Return first associated verb (not "look") or 0 if none found.
+*/
+char *HugoEngine::useBG(char *name) {
+ debugC(1, kDebugEngine, "useBG(%s)", name);
+
+ objectList_t p = _backgroundObjects[*_screen_p];
+ for (int i = 0; *_arrayVerbs[p[i].verbIndex]; i++) {
+ if ((name == _arrayNouns[p[i].nounIndex][0] &&
+ p[i].verbIndex != _look) &&
+ ((p[i].roomState == DONT_CARE) || (p[i].roomState == _screenStates[*_screen_p])))
+ return _arrayVerbs[p[i].verbIndex][0];
+ }
+
+ return 0;
+}
+
+/**
+* Add action lists for this screen to event queue
+*/
+void HugoEngine::screenActions(int screenNum) {
+ debugC(1, kDebugEngine, "screenActions(%d)", screenNum);
+
+ uint16 *screenAct = _screenActs[screenNum];
+ if (screenAct) {
+ for (int i = 0; screenAct[i]; i++)
+ _scheduler->insertActionList(screenAct[i]);
+ }
+}
+
+/**
+* Set the new screen number into the hero object and any carried objects
+*/
+void HugoEngine::setNewScreen(int screenNum) {
+ debugC(1, kDebugEngine, "setNewScreen(%d)", screenNum);
+
+ *_screen_p = screenNum; // HERO object
+ _object->setCarriedScreen(screenNum); // Carried objects
+}
+
+/**
+* An object has collided with a boundary. See if any actions are required
+*/
+void HugoEngine::boundaryCollision(object_t *obj) {
+ debugC(1, kDebugEngine, "boundaryCollision");
+
+ if (obj == _hero) {
+ // Hotspots only relevant to HERO
+ int x;
+ if (obj->vx > 0)
+ x = obj->x + obj->currImagePtr->x2;
+ else
+ x = obj->x + obj->currImagePtr->x1;
+ int y = obj->y + obj->currImagePtr->y2;
+
+ for (int i = 0; _hotspots[i].screenIndex >= 0; i++) {
+ hotspot_t *hotspot = &_hotspots[i];
+ if (hotspot->screenIndex == obj->screenIndex)
+ if ((x >= hotspot->x1) && (x <= hotspot->x2) && (y >= hotspot->y1) && (y <= hotspot->y2)) {
+ _scheduler->insertActionList(hotspot->actIndex);
+ break;
+ }
+ }
+ } else {
+ // Check whether an object collided with HERO
+ int dx = _hero->x + _hero->currImagePtr->x1 - obj->x - obj->currImagePtr->x1;
+ int dy = _hero->y + _hero->currImagePtr->y2 - obj->y - obj->currImagePtr->y2;
+ // If object's radius is infinity, use a closer value
+ int8 radius = obj->radius;
+ if (radius < 0)
+ radius = DX * 2;
+ if ((abs(dx) <= radius) && (abs(dy) <= radius))
+ _scheduler->insertActionList(obj->actIndex);
+ }
+}
+
+/**
+* Add up all the object values and all the bonus points
+*/
+void HugoEngine::calcMaxScore() {
+ debugC(1, kDebugEngine, "calcMaxScore");
+
+ _maxscore = _object->calcMaxScore();
+
+ for (int i = 0; i < _numBonuses; i++)
+ _maxscore += _points[i].score;
+}
+
+/**
+* Exit game, advertise trilogy, show copyright
+*/
+void HugoEngine::endGame() {
+ debugC(1, kDebugEngine, "endGame");
+
+ if (!_boot.registered)
+ Utils::Box(BOX_ANY, "%s", _textEngine[kEsAdvertise]);
+ Utils::Box(BOX_ANY, "%s\n%s", _episode, COPYRIGHT);
+ _status.viewState = V_EXIT;
+}
+
} // End of namespace Hugo
diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h
index 1d3657b473..d7dd767b6b 100644
--- a/engines/hugo/hugo.h
+++ b/engines/hugo/hugo.h
@@ -28,19 +28,37 @@
#include "engines/engine.h"
#include "common/file.h"
+#include "hugo/console.h"
// This include is here temporarily while the engine is being refactored.
#include "hugo/game.h"
-#define HUGO_DAT_VER_MAJ 0 // 1 byte
-#define HUGO_DAT_VER_MIN 25 // 1 byte
-#define DATAALIGNMENT 4
+#define HUGO_DAT_VER_MAJ 0 // 1 byte
+#define HUGO_DAT_VER_MIN 30 // 1 byte
+#define DATAALIGNMENT 4
+#define EDGE 10 // Closest object can get to edge of screen
+#define EDGE2 (EDGE * 2) // Push object further back on edge collision
+#define SHIFT 8 // Place hero this far inside bounding box
namespace Common {
class RandomSource;
}
+/**
+ * This is the namespace of the Hugo engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Hugo's House of Horror
+ * - Whodunit?
+ * - Jungle of Doom
+ * - Hugo's Horrific Adventure
+ * - Hugo's Mystery Adventure
+ * - Hugo's Amazon Adventure
+ */
namespace Hugo {
+
enum GameType {
kGameTypeNone = 0,
kGameTypeHugo1,
@@ -58,20 +76,25 @@ enum GameVariant {
};
enum HugoDebugChannels {
- kDebugSchedule = 1 << 0,
- kDebugEngine = 1 << 1,
- kDebugDisplay = 1 << 2,
- kDebugMouse = 1 << 3,
- kDebugParser = 1 << 4,
- kDebugFile = 1 << 5,
- kDebugRoute = 1 << 6,
- kDebugInventory = 1 << 7
+ kDebugSchedule = 1 << 0,
+ kDebugEngine = 1 << 1,
+ kDebugDisplay = 1 << 2,
+ kDebugMouse = 1 << 3,
+ kDebugParser = 1 << 4,
+ kDebugFile = 1 << 5,
+ kDebugRoute = 1 << 6,
+ kDebugInventory = 1 << 7,
+ kDebugObject = 1 << 8
};
enum HugoGameFeatures {
GF_PACKED = (1 << 0) // Database
};
+// Strings used by the engine
+enum seqTextEngine {
+ kEsAdvertise = 0
+};
struct HugoGameDescription;
class FileManager;
@@ -83,6 +106,8 @@ class Parser;
class Route;
class SoundHandler;
class IntroHandler;
+class ObjectHandler;
+
class HugoEngine : public Engine {
public:
@@ -93,21 +118,18 @@ public:
byte _gameVariant;
byte _maxInvent;
byte _numBonuses;
- byte _soundSilence;
- byte _soundTest;
- byte _tunesNbr;
+ int8 _soundSilence;
+ int8 _soundTest;
+ int8 _tunesNbr;
uint16 _numScreens;
object_t *_hero;
byte *_screen_p;
byte _heroImage;
- byte *_palette;
byte *_introX;
byte *_introY;
byte *_screenStates;
- byte *_arrayFont[3];
- int16 _arrayFontSize[3];
char **_textData;
char **_stringtData;
char **_screenNames;
@@ -115,7 +137,6 @@ public:
char **_textIntro;
char **_textMouse;
char **_textParser;
- char **_textSchedule;
char **_textUtil;
char ***_arrayNouns;
char ***_arrayVerbs;
@@ -123,26 +144,28 @@ public:
hotspot_t *_hotspots;
int16 *_invent;
uses_t *_uses;
+ uint16 _usesSize;
background_t *_catchallList;
background_t **_backgroundObjects;
+ uint16 _backgroundObjectsSize;
point_t *_points;
cmd **_cmdList;
+ uint16 _cmdListSize;
uint16 **_screenActs;
- object_t *_objects;
- act **_actListArr;
+ uint16 _screenActsSize;
int16 *_defltTunes;
uint16 _look;
uint16 _take;
uint16 _drop;
- uint16 _numObj;
- uint16 _alNewscrIndex;
+
+ GUI::Debugger *getDebugger() { return _console; }
Common::RandomSource *_rnd;
const char *_episode;
const char *_picDir;
- Common::String _initFilename, _saveFilename;
+ Common::String _saveFilename;
command_t _statusLine;
command_t _scoreLine;
@@ -160,34 +183,6 @@ public:
return *s_Engine;
}
- FileManager &file() {
- return *_fileManager;
- }
- Scheduler &scheduler() {
- return *_scheduler;
- }
- Screen &screen() {
- return *_screen;
- }
- MouseHandler &mouse() {
- return *_mouseHandler;
- }
- InventoryHandler &inventory() {
- return *_inventoryHandler;
- }
- Parser &parser() {
- return *_parser;
- }
- Route &route() {
- return *_route;
- }
- SoundHandler &sound() {
- return *_soundHandler;
- }
- IntroHandler &intro() {
- return *_introHandler;
- }
-
void initGame(const HugoGameDescription *gd);
void initGamePart(const HugoGameDescription *gd);
bool loadHugoDat();
@@ -199,23 +194,20 @@ public:
return _mouseY;
}
- void initStatus();
- void readObjectImages();
- void readUIFImages();
- void updateImages();
- void moveObjects();
- void useObject(int16 objId);
- bool findObjectSpace(object_t *obj, int16 *destx, int16 *desty);
- int16 findObject(uint16 x, uint16 y);
- void lookObject(object_t *obj);
- void storeBoundary(int x1, int x2, int y);
+ void boundaryCollision(object_t *obj);
void clearBoundary(int x1, int x2, int y);
void endGame();
+ void initStatus();
void readScreenFiles(int screen);
- void setNewScreen(int screen);
- void initNewScreenDisplay();
void screenActions(int screen);
+ void setNewScreen(int screen);
void shutdown();
+ void storeBoundary(int x1, int x2, int y);
+
+ char *useBG(char *name);
+
+ int deltaX(int x1, int x2, int vx, int y);
+ int deltaY(int x1, int x2, int vy, int y);
overlay_t &getBoundaryOverlay() {
return _boundary;
@@ -229,11 +221,9 @@ public:
overlay_t &getFirstOverlay() {
return _overlay;
}
-
status_t &getGameStatus() {
return _status;
}
-
int getScore() const {
return _score;
}
@@ -253,6 +243,17 @@ public:
return _introXSize;
}
+ FileManager *_file;
+ Scheduler *_scheduler;
+ Screen *_screen;
+ MouseHandler *_mouse;
+ InventoryHandler *_inventory;
+ Parser *_parser;
+ Route *_route;
+ SoundHandler *_sound;
+ IntroHandler *_intro;
+ ObjectHandler *_object;
+
protected:
// Engine APIs
@@ -261,12 +262,13 @@ protected:
private:
int _mouseX;
int _mouseY;
- byte _paletteSize;
byte _introXSize;
status_t _status; // Game status structure
static HugoEngine *s_Engine;
+ HugoConsole *_console;
+
// The following are bit plane display overlays which mark travel boundaries,
// foreground stationary objects and baselines for those objects (used to
// determine foreground/background wrt moving objects)
@@ -274,27 +276,17 @@ private:
// Vinterstum: These shouldn't be static, but we get weird pathfinding issues (and Valgrind warnings) without.
// Needs more investigation. Alignment issues?
- static overlay_t _boundary; // Boundary overlay file
- static overlay_t _overlay; // First overlay file
- static overlay_t _ovlBase; // First overlay base file
- static overlay_t _objBound; // Boundary file marks object baselines
+ static overlay_t _boundary; // Boundary overlay file
+ static overlay_t _overlay; // First overlay file
+ static overlay_t _ovlBase; // First overlay base file
+ static overlay_t _objBound; // Boundary file marks object baselines
GameType _gameType;
Common::Platform _platform;
bool _packedFl;
- FileManager *_fileManager;
- Scheduler *_scheduler;
- Screen *_screen;
- MouseHandler *_mouseHandler;
- InventoryHandler *_inventoryHandler;
- Parser *_parser;
- Route *_route;
- SoundHandler *_soundHandler;
- IntroHandler *_introHandler;
-
- int _score; // Holds current score
- int _maxscore; // Holds maximum score
+ int _score; // Holds current score
+ int _maxscore; // Holds maximum score
char **loadTextsVariante(Common::File &in, uint16 *arraySize);
char ***loadTextsArray(Common::File &in);
@@ -305,19 +297,10 @@ private:
void initPlaylist(bool playlist[MAX_TUNES]);
void initConfig(inst_t action);
void initialize();
- int deltaX(int x1, int x2, int vx, int y);
- int deltaY(int x1, int x2, int vy, int y);
- void processMaze();
- //int y2comp (const void *a, const void *b);
- char *useBG(char *name);
- void freeObjects();
- void boundaryCollision(object_t *obj);
void calcMaxScore();
void initMachine();
void runMachine();
- static int y2comp(const void *a, const void *b);
-
};
} // End of namespace Hugo
diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp
index b818bef027..d63a979fe1 100644
--- a/engines/hugo/intro.cpp
+++ b/engines/hugo/intro.cpp
@@ -37,7 +37,7 @@
namespace Hugo {
-IntroHandler::IntroHandler(HugoEngine &vm) : _vm(vm) {
+IntroHandler::IntroHandler(HugoEngine *vm) : _vm(vm) {
}
IntroHandler::~IntroHandler() {
diff --git a/engines/hugo/intro.h b/engines/hugo/intro.h
index f8f1f95514..f1de01e609 100644
--- a/engines/hugo/intro.h
+++ b/engines/hugo/intro.h
@@ -43,7 +43,7 @@ enum seqTextIntro {
class IntroHandler {
public:
- IntroHandler(HugoEngine &vm);
+ IntroHandler(HugoEngine *vm);
virtual ~IntroHandler();
virtual void preNewGame() = 0;
@@ -51,13 +51,13 @@ public:
virtual bool introPlay() = 0;
protected:
- HugoEngine &_vm;
+ HugoEngine *_vm;
int16 introTicks; // Count calls to introPlay()
};
class intro_v1w : public IntroHandler {
public:
- intro_v1w(HugoEngine &vm);
+ intro_v1w(HugoEngine *vm);
~intro_v1w();
void preNewGame();
@@ -67,7 +67,7 @@ public:
class intro_v1d : public IntroHandler {
public:
- intro_v1d(HugoEngine &vm);
+ intro_v1d(HugoEngine *vm);
~intro_v1d();
void preNewGame();
@@ -77,7 +77,7 @@ public:
class intro_v2w : public IntroHandler {
public:
- intro_v2w(HugoEngine &vm);
+ intro_v2w(HugoEngine *vm);
~intro_v2w();
void preNewGame();
@@ -87,7 +87,7 @@ public:
class intro_v2d : public IntroHandler {
public:
- intro_v2d(HugoEngine &vm);
+ intro_v2d(HugoEngine *vm);
~intro_v2d();
void preNewGame();
@@ -97,7 +97,7 @@ public:
class intro_v3w : public IntroHandler {
public:
- intro_v3w(HugoEngine &vm);
+ intro_v3w(HugoEngine *vm);
~intro_v3w();
void preNewGame();
@@ -107,7 +107,7 @@ public:
class intro_v3d : public IntroHandler {
public:
- intro_v3d(HugoEngine &vm);
+ intro_v3d(HugoEngine *vm);
~intro_v3d();
void preNewGame();
diff --git a/engines/hugo/intro_v1d.cpp b/engines/hugo/intro_v1d.cpp
index 0e3067f0e9..61626e8172 100644
--- a/engines/hugo/intro_v1d.cpp
+++ b/engines/hugo/intro_v1d.cpp
@@ -37,7 +37,7 @@
#include "hugo/display.h"
namespace Hugo {
-intro_v1d::intro_v1d(HugoEngine &vm) : IntroHandler(vm) {
+intro_v1d::intro_v1d(HugoEngine *vm) : IntroHandler(vm) {
}
intro_v1d::~intro_v1d() {
@@ -52,116 +52,116 @@ void intro_v1d::introInit() {
bool intro_v1d::introPlay() {
static int state = 0;
- byte introSize = _vm.getIntroSize();
+ byte introSize = _vm->getIntroSize();
if (introTicks < introSize) {
switch (state++) {
case 0:
- _vm.screen().drawRectangle(true, 0, 0, 319, 199, _TMAGENTA);
- _vm.screen().drawRectangle(true, 10, 10, 309, 189, _TBLACK);
+ _vm->_screen->drawRectangle(true, 0, 0, 319, 199, _TMAGENTA);
+ _vm->_screen->drawRectangle(true, 10, 10, 309, 189, _TBLACK);
break;
case 1:
- _vm.screen().drawShape(20, 92,_TLIGHTMAGENTA,_TMAGENTA);
- _vm.screen().drawShape(250,92,_TLIGHTMAGENTA,_TMAGENTA);
+ _vm->_screen->drawShape(20, 92,_TLIGHTMAGENTA,_TMAGENTA);
+ _vm->_screen->drawShape(250,92,_TLIGHTMAGENTA,_TMAGENTA);
// HACK: use of TROMAN, size 10-5
- _vm.screen().loadFont(0);
+ _vm->_screen->loadFont(0);
char buffer[80];
if (_boot.registered)
strcpy(buffer, "Registered Version");
else
strcpy(buffer, "Shareware Version");
- _vm.screen().writeStr(CENTER, 163, buffer, _TLIGHTMAGENTA);
- _vm.screen().writeStr(CENTER, 176, COPYRIGHT, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 163, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 176, COPYRIGHT, _TLIGHTMAGENTA);
if (scumm_stricmp(_boot.distrib, "David P. Gray")) {
sprintf(buffer, "Distributed by %s.", _boot.distrib);
- _vm.screen().writeStr(CENTER, 75, buffer, _TMAGENTA);
+ _vm->_screen->writeStr(CENTER, 75, buffer, _TMAGENTA);
}
// HACK: use of SCRIPT size 24-16
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "Hugo's");
- _vm.screen().writeStr(CENTER, 20, buffer, _TMAGENTA);
+ _vm->_screen->writeStr(CENTER, 20, buffer, _TMAGENTA);
// HACK: use of TROMAN, size 30-24
strcpy(buffer, "House of Horrors !");
- _vm.screen().writeStr(CENTER, 50, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 50, buffer, _TLIGHTMAGENTA);
break;
case 2:
- _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ _vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
// HACK: use of TROMAN, size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "S t a r r i n g :");
- _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ _vm->_screen->writeStr(CENTER, 95, buffer, _TMAGENTA);
break;
case 3:
// HACK: use of TROMAN size 20-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "Hugo !");
- _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
break;
case 4:
- _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ _vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
// HACK: use of TROMAN size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "P r o d u c e d b y :");
- _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ _vm->_screen->writeStr(CENTER, 95, buffer, _TMAGENTA);
break;
case 5:
// HACK: use of TROMAN size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "David P Gray !");
- _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
break;
case 6:
- _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ _vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
// HACK: use of TROMAN size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "D i r e c t e d b y :");
- _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ _vm->_screen->writeStr(CENTER, 95, buffer, _TMAGENTA);
break;
case 7:
// HACK: use of TROMAN size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "David P Gray !");
- _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
break;
case 8:
- _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ _vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
// HACK: use of TROMAN size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "M u s i c b y :");
- _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ _vm->_screen->writeStr(CENTER, 95, buffer, _TMAGENTA);
break;
case 9:
// HACK: use of TROMAN size 16-9
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "David P Gray !");
- _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
break;
case 10:
- _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ _vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
// HACK: use of TROMAN size 20-14
- _vm.screen().loadFont(2);
+ _vm->_screen->loadFont(2);
strcpy(buffer, "E n j o y !");
- _vm.screen().writeStr(CENTER, 100, buffer, _TLIGHTMAGENTA);
+ _vm->_screen->writeStr(CENTER, 100, buffer, _TLIGHTMAGENTA);
break;
}
- _vm.screen().displayBackground();
+ _vm->_screen->displayBackground();
g_system->updateScreen();
g_system->delayMillis(1000);
}
diff --git a/engines/hugo/intro_v1w.cpp b/engines/hugo/intro_v1w.cpp
index 38921fd3e4..923cd3658f 100644
--- a/engines/hugo/intro_v1w.cpp
+++ b/engines/hugo/intro_v1w.cpp
@@ -39,16 +39,17 @@
namespace Hugo {
-intro_v1w::intro_v1w(HugoEngine &vm) : IntroHandler(vm) {
+intro_v1w::intro_v1w(HugoEngine *vm) : IntroHandler(vm) {
}
intro_v1w::~intro_v1w() {
}
+/**
+* Auto-start a new game
+*/
void intro_v1w::preNewGame() {
- // Auto-start a new game
- _vm.file().restoreGame(-1);
- _vm.getGameStatus().viewState = V_INTROINIT;
+ _vm->getGameStatus().viewState = V_INTROINIT;
}
void intro_v1w::introInit() {
diff --git a/engines/hugo/intro_v2d.cpp b/engines/hugo/intro_v2d.cpp
index bfae11b77e..0c9f85d1ea 100644
--- a/engines/hugo/intro_v2d.cpp
+++ b/engines/hugo/intro_v2d.cpp
@@ -39,7 +39,7 @@
namespace Hugo {
-intro_v2d::intro_v2d(HugoEngine &vm) : IntroHandler(vm) {
+intro_v2d::intro_v2d(HugoEngine *vm) : IntroHandler(vm) {
}
intro_v2d::~intro_v2d() {
@@ -49,8 +49,8 @@ void intro_v2d::preNewGame() {
}
void intro_v2d::introInit() {
- _vm.screen().loadFont(0);
- _vm.file().readBackground(_vm._numScreens - 1); // display splash screen
+ _vm->_screen->loadFont(0);
+ _vm->_file->readBackground(_vm->_numScreens - 1); // display splash screen
char buffer[128];
@@ -58,14 +58,14 @@ void intro_v2d::introInit() {
sprintf(buffer, "%s Registered Version", COPYRIGHT);
else
sprintf(buffer, "%s Shareware Version", COPYRIGHT);
- _vm.screen().writeStr(CENTER, 186, buffer, _TLIGHTRED);
+ _vm->_screen->writeStr(CENTER, 186, buffer, _TLIGHTRED);
if (scumm_stricmp(_boot.distrib, "David P. Gray")) {
sprintf(buffer, "Distributed by %s.", _boot.distrib);
- _vm.screen().writeStr(CENTER, 1, buffer, _TLIGHTRED);
+ _vm->_screen->writeStr(CENTER, 1, buffer, _TLIGHTRED);
}
- _vm.screen().displayBackground();
+ _vm->_screen->displayBackground();
g_system->updateScreen();
g_system->delayMillis(5000);
}
diff --git a/engines/hugo/intro_v2w.cpp b/engines/hugo/intro_v2w.cpp
index f68761eac9..414846bfc7 100644
--- a/engines/hugo/intro_v2w.cpp
+++ b/engines/hugo/intro_v2w.cpp
@@ -38,7 +38,7 @@
namespace Hugo {
-intro_v2w::intro_v2w(HugoEngine &vm) : IntroHandler(vm) {
+intro_v2w::intro_v2w(HugoEngine *vm) : IntroHandler(vm) {
}
intro_v2w::~intro_v2w() {
diff --git a/engines/hugo/intro_v3d.cpp b/engines/hugo/intro_v3d.cpp
index 3dd2a58d8b..87dca946f5 100644
--- a/engines/hugo/intro_v3d.cpp
+++ b/engines/hugo/intro_v3d.cpp
@@ -40,7 +40,7 @@
namespace Hugo {
-intro_v3d::intro_v3d(HugoEngine &vm) : IntroHandler(vm) {
+intro_v3d::intro_v3d(HugoEngine *vm) : IntroHandler(vm) {
}
intro_v3d::~intro_v3d() {
@@ -50,8 +50,8 @@ void intro_v3d::preNewGame() {
}
void intro_v3d::introInit() {
- _vm.screen().loadFont(0);
- _vm.file().readBackground(_vm._numScreens - 1); // display splash screen
+ _vm->_screen->loadFont(0);
+ _vm->_file->readBackground(_vm->_numScreens - 1); // display splash screen
char buffer[128];
if (_boot.registered)
@@ -59,43 +59,45 @@ void intro_v3d::introInit() {
else
sprintf(buffer,"%s Shareware Version", COPYRIGHT);
- _vm.screen().writeStr(CENTER, 190, buffer, _TBROWN);
+ _vm->_screen->writeStr(CENTER, 190, buffer, _TBROWN);
if (scumm_stricmp(_boot.distrib, "David P. Gray")) {
sprintf(buffer, "Distributed by %s.", _boot.distrib);
- _vm.screen().writeStr(CENTER, 0, buffer, _TBROWN);
+ _vm->_screen->writeStr(CENTER, 0, buffer, _TBROWN);
}
- _vm.screen().displayBackground();
+ _vm->_screen->displayBackground();
g_system->updateScreen();
g_system->delayMillis(5000);
-
- _vm.file().readBackground(22); // display screen MAP_3d
- _vm.screen().displayBackground();
+
+ _vm->_file->readBackground(22); // display screen MAP_3d
+ _vm->_screen->displayBackground();
introTicks = 0;
}
+/**
+* Hugo 3 - Preamble screen before going into game. Draws path of Hugo's plane.
+* Called every tick. Returns TRUE when complete
+*/
bool intro_v3d::introPlay() {
- byte introSize = _vm.getIntroSize();
+ byte introSize = _vm->getIntroSize();
-// Hugo 3 - Preamble screen before going into game. Draws path of Hugo's plane.
-// Called every tick. Returns TRUE when complete
//TODO : Add proper check of story mode
//#if STORY
if (introTicks < introSize) {
- _vm.screen().writeStr(_vm._introX[introTicks], _vm._introY[introTicks] - DIBOFF_Y, "x", _TBRIGHTWHITE);
- _vm.screen().displayBackground();
+ _vm->_screen->writeStr(_vm->_introX[introTicks], _vm->_introY[introTicks] - DIBOFF_Y, "x", _TBRIGHTWHITE);
+ _vm->_screen->displayBackground();
// Text boxes at various times
switch (introTicks) {
case 4:
- Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro1]);
+ Utils::Box(BOX_OK, "%s", _vm->_textIntro[kIntro1]);
break;
case 9:
- Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro2]);
+ Utils::Box(BOX_OK, "%s", _vm->_textIntro[kIntro2]);
break;
case 35:
- Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro3]);
+ Utils::Box(BOX_OK, "%s", _vm->_textIntro[kIntro3]);
break;
}
}
diff --git a/engines/hugo/intro_v3w.cpp b/engines/hugo/intro_v3w.cpp
index 924fa46805..06fe1814fe 100644
--- a/engines/hugo/intro_v3w.cpp
+++ b/engines/hugo/intro_v3w.cpp
@@ -40,7 +40,7 @@
namespace Hugo {
-intro_v3w::intro_v3w(HugoEngine &vm) : IntroHandler(vm) {
+intro_v3w::intro_v3w(HugoEngine *vm) : IntroHandler(vm) {
}
intro_v3w::~intro_v3w() {
@@ -49,39 +49,43 @@ intro_v3w::~intro_v3w() {
void intro_v3w::preNewGame() {
}
+/**
+* Hugo 3 - show map and set up for introPlay()
+*/
void intro_v3w::introInit() {
-// Hugo 3 - show map and set up for introPlay()
//#if STORY
- _vm.file().readBackground(22); // display screen MAP_3w
- _vm.screen().displayBackground();
+ _vm->_file->readBackground(22); // display screen MAP_3w
+ _vm->_screen->displayBackground();
introTicks = 0;
- _vm.screen().loadFont(0);
+ _vm->_screen->loadFont(0);
//#endif
}
+/**
+* Hugo 3 - Preamble screen before going into game. Draws path of Hugo's plane.
+* Called every tick. Returns TRUE when complete
+*/
bool intro_v3w::introPlay() {
- byte introSize = _vm.getIntroSize();
+ byte introSize = _vm->getIntroSize();
-// Hugo 3 - Preamble screen before going into game. Draws path of Hugo's plane.
-// Called every tick. Returns TRUE when complete
//TODO : Add proper check of story mode
//#if STORY
if (introTicks < introSize) {
// Scale viewport x_intro,y_intro to screen (offsetting y)
- _vm.screen().writeStr(_vm._introX[introTicks], _vm._introY[introTicks] - DIBOFF_Y, "x", _TBRIGHTWHITE);
- _vm.screen().displayBackground();
+ _vm->_screen->writeStr(_vm->_introX[introTicks], _vm->_introY[introTicks] - DIBOFF_Y, "x", _TBRIGHTWHITE);
+ _vm->_screen->displayBackground();
// Text boxes at various times
switch (introTicks) {
case 4:
- Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro1]);
+ Utils::Box(BOX_OK, "%s", _vm->_textIntro[kIntro1]);
break;
case 9:
- Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro2]);
+ Utils::Box(BOX_OK, "%s", _vm->_textIntro[kIntro2]);
break;
case 35:
- Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro3]);
+ Utils::Box(BOX_OK, "%s", _vm->_textIntro[kIntro3]);
break;
}
}
diff --git a/engines/hugo/inventory.cpp b/engines/hugo/inventory.cpp
index 5046d191c3..19aa69c1fa 100644
--- a/engines/hugo/inventory.cpp
+++ b/engines/hugo/inventory.cpp
@@ -40,29 +40,32 @@
#include "hugo/mouse.h"
#include "hugo/inventory.h"
#include "hugo/parser.h"
+#include "hugo/object.h"
namespace Hugo {
#define MAX_DISP (XPIX / INV_DX) // Max icons displayable
-InventoryHandler::InventoryHandler(HugoEngine &vm) : _vm(vm) {
+InventoryHandler::InventoryHandler(HugoEngine *vm) : _vm(vm) {
}
-// Construct the inventory scrollbar in dib_i
-// imageTotNumb is total number of inventory icons
-// displayNumb is number requested for display
-// scrollFl is TRUE if scroll arrows required
-// firstObjId is index of first (scrolled) inventory object to display
+/**
+* Construct the inventory scrollbar in dib_i
+* imageTotNumb is total number of inventory icons
+* displayNumb is number requested for display
+* scrollFl is TRUE if scroll arrows required
+* firstObjId is index of first (scrolled) inventory object to display
+*/
void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId) {
debugC(1, kDebugInventory, "constructInventory(%d, %d, %d, %d)", imageTotNumb, displayNumb, (scrollFl) ? 0 : 1, firstObjId);
// Clear out icon buffer
- memset(_vm.screen().getIconBuffer(), 0, sizeof(_vm.screen().getIconBuffer()));
+ memset(_vm->_screen->getIconBuffer(), 0, sizeof(_vm->_screen->getIconBuffer()));
// If needed, copy arrows - reduce number of icons displayable
if (scrollFl) { // Display at first and last icon positions
- _vm.screen().moveImage(_vm.screen().getGUIBuffer(), 0, 0, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), 0, 0, XPIX);
- _vm.screen().moveImage(_vm.screen().getGUIBuffer(), INV_DX, 0, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), INV_DX *(MAX_DISP - 1), 0, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getGUIBuffer(), 0, 0, INV_DX, INV_DY, XPIX, _vm->_screen->getIconBuffer(), 0, 0, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getGUIBuffer(), INV_DX, 0, INV_DX, INV_DY, XPIX, _vm->_screen->getIconBuffer(), INV_DX *(MAX_DISP - 1), 0, XPIX);
displayNumb = MIN(displayNumb, MAX_DISP - NUM_ARROWS);
} else // No, override first index - we can show 'em all!
firstObjId = 0;
@@ -71,7 +74,7 @@ void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, b
int16 displayed = 0;
int16 carried = 0;
for (int16 i = 0; i < imageTotNumb; i++) {
- if (_vm._objects[_vm._invent[i]].carriedFl) {
+ if (_vm->_object->isCarried(_vm->_invent[i])) {
// Check still room to display and past first scroll index
if (displayed < displayNumb && carried >= firstObjId) {
// Compute source coordinates in dib_u
@@ -80,18 +83,20 @@ void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, b
// Compute dest coordinates in dib_i
int16 ix = ((scrollFl) ? displayed + 1 : displayed) * INV_DX;
- displayed++; // Count number displayed
+ displayed++; // Count number displayed
// Copy the icon
- _vm.screen().moveImage(_vm.screen().getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), ix, 0, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm->_screen->getIconBuffer(), ix, 0, XPIX);
}
carried++; // Count number carried
}
}
}
-// Process required action for inventory
-// Returns objId under cursor (or -1) for INV_GET
+/**
+* Process required action for inventory
+* Returns objId under cursor (or -1) for INV_GET
+*/
int16 InventoryHandler::processInventory(invact_t action, ...) {
debugC(1, kDebugInventory, "processInventory(invact_t action, ...)");
@@ -100,8 +105,8 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
int16 imageNumb; // Total number of inventory items
int displayNumb; // Total number displayed/carried
// Compute total number and number displayed, i.e. number carried
- for (imageNumb = 0, displayNumb = 0; imageNumb < _vm._maxInvent && _vm._invent[imageNumb] != -1; imageNumb++) {
- if (_vm._objects[_vm._invent[imageNumb]].carriedFl)
+ for (imageNumb = 0, displayNumb = 0; imageNumb < _vm->_maxInvent && _vm->_invent[imageNumb] != -1; imageNumb++) {
+ if (_vm->_object->isCarried(_vm->_invent[imageNumb]))
displayNumb++;
}
@@ -109,7 +114,7 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
bool scrollFl = displayNumb > MAX_DISP;
va_list marker; // Args used for D_ADD operation
int16 cursorx, cursory; // Current cursor position
- int16 objId = -1; // Return objid under cursor
+ int16 objId = -1; // Return objid under cursor
switch (action) {
case INV_INIT: // Initialize inventory display
@@ -148,8 +153,8 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
if (objId == -1 && i < displayNumb) {
// Find objid by counting # carried objects == i+1
int16 j;
- for (j = 0, i++; i > 0 && j < _vm._numObj; j++) {
- if (_vm._objects[j].carriedFl) {
+ for (j = 0, i++; i > 0 && j < _vm->_object->_numObj; j++) {
+ if (_vm->_object->isCarried(j)) {
if (--i == 0)
objId = j;
}
@@ -158,15 +163,17 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
}
break;
}
- return objId; // For the INV_GET action
+ return objId; // For the INV_GET action
}
+/**
+* Process inventory state machine
+*/
void InventoryHandler::runInventory() {
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
debugC(1, kDebugInventory, "runInventory");
-// Process inventory state machine
switch (gameStatus.inventoryState) {
case I_OFF: // Icon bar off screen
break;
@@ -176,15 +183,15 @@ void InventoryHandler::runInventory() {
gameStatus.inventoryHeight = 0;
// Move visible portion to _frontBuffer, restore uncovered portion, display results
- _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX);
- _vm.screen().moveImage(_vm.screen().getBackBufferBackup(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX, STEP_DY, XPIX, _vm.screen().getFrontBuffer(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX);
- _vm.screen().displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight + STEP_DY);
+ _vm->_screen->moveImage(_vm->_screen->getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm->_screen->getFrontBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getBackBufferBackup(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX, STEP_DY, XPIX, _vm->_screen->getFrontBuffer(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX);
+ _vm->_screen->displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight + STEP_DY);
if (gameStatus.inventoryHeight == 0) { // Finished moving up?
// Yes, restore dibs and exit back to game state machine
- _vm.screen().moveImage(_vm.screen().getBackBufferBackup(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBuffer(), 0, 0, XPIX);
- _vm.screen().moveImage(_vm.screen().getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getFrontBuffer(), 0, 0, XPIX);
- _vm.updateImages(); // Add objects back into display list for restore
+ _vm->_screen->moveImage(_vm->_screen->getBackBufferBackup(), 0, 0, XPIX, YPIX, XPIX, _vm->_screen->getBackBuffer(), 0, 0, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm->_screen->getFrontBuffer(), 0, 0, XPIX);
+ _vm->_object->updateImages(); // Add objects back into display list for restore
gameStatus.inventoryState = I_OFF;
gameStatus.viewState = V_PLAY;
}
@@ -194,9 +201,9 @@ void InventoryHandler::runInventory() {
// and get any icon/text out of _frontBuffer
if (gameStatus.inventoryHeight == 0) {
processInventory(INV_INIT); // Initialize dib_i
- _vm.screen().displayList(D_RESTORE); // Restore _frontBuffer
- _vm.updateImages(); // Rebuild _frontBuffer without icons/text
- _vm.screen().displayList(D_DISPLAY); // Blit display list to screen
+ _vm->_screen->displayList(D_RESTORE); // Restore _frontBuffer
+ _vm->_object->updateImages(); // Rebuild _frontBuffer without icons/text
+ _vm->_screen->displayList(D_DISPLAY); // Blit display list to screen
}
gameStatus.inventoryHeight += STEP_DY; // Move the icon bar down
@@ -204,8 +211,8 @@ void InventoryHandler::runInventory() {
gameStatus.inventoryHeight = INV_DY;
// Move visible portion to _frontBuffer, display results
- _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX);
- _vm.screen().displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight);
+ _vm->_screen->moveImage(_vm->_screen->getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm->_screen->getFrontBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm->_screen->displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight);
if (gameStatus.inventoryHeight == INV_DY) { // Finished moving down?
// Yes, prepare view dibs for special inventory display since
@@ -213,17 +220,17 @@ void InventoryHandler::runInventory() {
// 1. Save backing store _backBuffer in temporary dib_c
// 2. Make snapshot of _frontBuffer the new _backBuffer backing store
// 3. Reset the display list
- _vm.screen().moveImage(_vm.screen().getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBufferBackup(), 0, 0, XPIX);
- _vm.screen().moveImage(_vm.screen().getFrontBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBuffer(), 0, 0, XPIX);
- _vm.screen().displayList(D_INIT);
+ _vm->_screen->moveImage(_vm->_screen->getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm->_screen->getBackBufferBackup(), 0, 0, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getFrontBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm->_screen->getBackBuffer(), 0, 0, XPIX);
+ _vm->_screen->displayList(D_INIT);
gameStatus.inventoryState = I_ACTIVE;
}
break;
case I_ACTIVE: // Inventory active
- _vm.parser().charHandler(); // Still allow commands
- _vm.screen().displayList(D_RESTORE); // Restore previous background
- _vm.mouse().mouseHandler(); // Mouse activity - adds to display list
- _vm.screen().displayList(D_DISPLAY); // Blit the display list to screen
+ _vm->_parser->charHandler(); // Still allow commands
+ _vm->_screen->displayList(D_RESTORE); // Restore previous background
+ _vm->_mouse->mouseHandler(); // Mouse activity - adds to display list
+ _vm->_screen->displayList(D_DISPLAY); // Blit the display list to screen
break;
}
}
diff --git a/engines/hugo/inventory.h b/engines/hugo/inventory.h
index 5cc1af28c2..36fca71a43 100644
--- a/engines/hugo/inventory.h
+++ b/engines/hugo/inventory.h
@@ -40,13 +40,13 @@ namespace Hugo {
class InventoryHandler {
public:
- InventoryHandler(HugoEngine &vm);
+ InventoryHandler(HugoEngine *vm);
int16 processInventory(invact_t action, ...);
void runInventory();
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
void constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId);
};
diff --git a/engines/hugo/module.mk b/engines/hugo/module.mk
index b646a3672d..703a70ec1f 100644
--- a/engines/hugo/module.mk
+++ b/engines/hugo/module.mk
@@ -1,11 +1,11 @@
MODULE := engines/hugo
MODULE_OBJS := \
+ console.o \
detection.o \
display.o \
display_v1d.o \
display_v1w.o \
- engine.o \
file.o \
file_v1d.o \
file_v2d.o \
@@ -21,6 +21,11 @@ MODULE_OBJS := \
intro_v3w.o \
inventory.o \
mouse.o \
+ object.o \
+ object_v1d.o \
+ object_v1w.o \
+ object_v2d.o \
+ object_v3d.o \
parser.o \
parser_v1w.o \
parser_v1d.o \
@@ -29,6 +34,8 @@ MODULE_OBJS := \
route.o \
schedule.o \
schedule_v1d.o \
+ schedule_v1w.o \
+ schedule_v2d.o \
schedule_v3d.o \
sound.o \
util.o
diff --git a/engines/hugo/mouse.cpp b/engines/hugo/mouse.cpp
index b305489568..385dce2703 100644
--- a/engines/hugo/mouse.cpp
+++ b/engines/hugo/mouse.cpp
@@ -43,6 +43,7 @@
#include "hugo/inventory.h"
#include "hugo/route.h"
#include "hugo/util.h"
+#include "hugo/object.h"
namespace Hugo {
@@ -59,34 +60,38 @@ enum seqTextMouse {
kMsExit = 1
};
-MouseHandler::MouseHandler(HugoEngine &vm) : _vm(vm) {
+MouseHandler::MouseHandler(HugoEngine *vm) : _vm(vm) {
}
-// Shadow-blit supplied string into dib_a at cx,cy and add to display list
+/**
+* Shadow-blit supplied string into dib_a at cx,cy and add to display list
+*/
void MouseHandler::cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color) {
debugC(1, kDebugMouse, "cursorText(%s, %d, %d, %d, %d)", buffer, cx, cy, fontId, color);
- _vm.screen().loadFont(fontId);
+ _vm->_screen->loadFont(fontId);
// Find bounding rect for string
- int16 sdx = _vm.screen().stringLength(buffer);
- int16 sdy = _vm.screen().fontHeight() + 1; // + 1 for shadow
+ int16 sdx = _vm->_screen->stringLength(buffer);
+ int16 sdy = _vm->_screen->fontHeight() + 1; // + 1 for shadow
int16 sx = (cx < XPIX / 2) ? cx + SX_OFF : cx - sdx - SX_OFF / 2;
int16 sy = cy + SY_OFF;
// Display the string and add rect to display list
- _vm.screen().shadowStr(sx, sy, buffer, _TBRIGHTWHITE);
- _vm.screen().displayList(D_ADD, sx, sy, sdx, sdy);
+ _vm->_screen->shadowStr(sx, sy, buffer, _TBRIGHTWHITE);
+ _vm->_screen->displayList(D_ADD, sx, sy, sdx, sdy);
}
-// Find the exit hotspot containing cx, cy.
-// Return hotspot index or -1 if not found.
+/**
+* Find the exit hotspot containing cx, cy.
+* Return hotspot index or -1 if not found.
+*/
int16 MouseHandler::findExit(int16 cx, int16 cy) {
debugC(2, kDebugMouse, "findExit(%d, %d)", cx, cy);
int i = 0;
- for (hotspot_t *hotspot = _vm._hotspots; hotspot->screenIndex >= 0; i++, hotspot++) {
- if (hotspot->screenIndex == *_vm._screen_p) {
+ for (hotspot_t *hotspot = _vm->_hotspots; hotspot->screenIndex >= 0; i++, hotspot++) {
+ if (hotspot->screenIndex == *_vm->_screen_p) {
if (cx >= hotspot->x1 && cx <= hotspot->x2 && cy >= hotspot->y1 && cy <= hotspot->y2)
return i;
}
@@ -94,13 +99,15 @@ int16 MouseHandler::findExit(int16 cx, int16 cy) {
return -1;
}
-// Process a mouse right click at coord cx, cy over object objid
+/**
+* Process a mouse right click at coord cx, cy over object objid
+*/
void MouseHandler::processRightClick(int16 objId, int16 cx, int16 cy) {
debugC(1, kDebugMouse, "Process_rclick(%d, %d, %d)", objId, cx, cy);
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
- if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET) // Make sure user has control
+ if (gameStatus.storyModeFl || _vm->_hero->pathType == QUIET) // Make sure user has control
return;
bool foundFl = false; // TRUE if route found to object
@@ -111,79 +118,80 @@ void MouseHandler::processRightClick(int16 objId, int16 cx, int16 cy) {
else if (gameStatus.inventoryObjId == objId)
gameStatus.inventoryObjId = -1; // Same icon - deselect it
else
- _vm.useObject(objId); // Use status.objid on object
+ _vm->_object->useObject(objId); // Use status.objid on object
} else { // Clicked over viewport object
- object_t *obj = &_vm._objects[objId];
+ object_t *obj = &_vm->_object->_objects[objId];
int16 x, y;
switch (obj->viewx) { // Where to walk to
case -1: // Walk to object position
- if (_vm.findObjectSpace(obj, &x, &y))
- foundFl = _vm.route().startRoute(GO_GET, objId, x, y);
+ if (_vm->_object->findObjectSpace(obj, &x, &y))
+ foundFl = _vm->_route->startRoute(GO_GET, objId, x, y);
if (!foundFl) // Can't get there, try to use from here
- _vm.useObject(objId);
+ _vm->_object->useObject(objId);
break;
case 0: // Immediate use
- _vm.useObject(objId); // Pick up or use object
+ _vm->_object->useObject(objId); // Pick up or use object
break;
default: // Walk to view point if possible
- if (!_vm.route().startRoute(GO_GET, objId, obj->viewx, obj->viewy)) {
- if (_vm._hero->cycling == INVISIBLE)// If invisible do
- _vm.useObject(objId); // immediate use
+ if (!_vm->_route->startRoute(GO_GET, objId, obj->viewx, obj->viewy)) {
+ if (_vm->_hero->cycling == INVISIBLE)// If invisible do
+ _vm->_object->useObject(objId); // immediate use
else
- Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ Utils::Box(BOX_ANY, "%s", _vm->_textMouse[kMsNoWayText]); // Can't get there
}
break;
}
}
}
-// Process a left mouse click over:
-// 1. An icon - show description
-// 2. An object - walk to and show description
-// 3. An icon scroll arrow - scroll the iconbar
-// 4. Nothing - attempt to walk there
-// 5. Exit - walk to exit hotspot
+/** Process a left mouse click over:
+* 1. An icon - show description
+* 2. An object - walk to and show description
+* 3. An icon scroll arrow - scroll the iconbar
+* 4. Nothing - attempt to walk there
+* 5. Exit - walk to exit hotspot
+*/
void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) {
debugC(1, kDebugMouse, "Process_lclick(%d, %d, %d)", objId, cx, cy);
int16 i, x, y;
object_t *obj;
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
- if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET) // Make sure user has control
+ if (gameStatus.storyModeFl || _vm->_hero->pathType == QUIET) // Make sure user has control
return;
switch (objId) {
case -1: // Empty space - attempt to walk there
- _vm.route().startRoute(GO_SPACE, 0, cx, cy);
+ _vm->_route->startRoute(GO_SPACE, 0, cx, cy);
break;
case LEFT_ARROW: // A scroll arrow - scroll the iconbar
case RIGHT_ARROW:
// Scroll the iconbar and display results
- _vm.inventory().processInventory((objId == LEFT_ARROW) ? INV_LEFT : INV_RIGHT);
- _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX);
- _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm.screen().getBackBuffer(), 0, DIBOFF_Y, XPIX);
- _vm.screen().displayList(D_ADD, 0, DIBOFF_Y, XPIX, INV_DY);
+ _vm->_inventory->processInventory((objId == LEFT_ARROW) ? INV_LEFT : INV_RIGHT);
+ _vm->_screen->moveImage(_vm->_screen->getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm->_screen->getFrontBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm->_screen->moveImage(_vm->_screen->getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm->_screen->getBackBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm->_screen->displayList(D_ADD, 0, DIBOFF_Y, XPIX, INV_DY);
break;
case EXIT_HOTSPOT: // Walk to exit hotspot
i = findExit(cx, cy);
- x = _vm._hotspots[i].viewx;
- y = _vm._hotspots[i].viewy;
+ x = _vm->_hotspots[i].viewx;
+ y = _vm->_hotspots[i].viewy;
if (x >= 0) { // Hotspot refers to an exit
// Special case of immediate exit
if (gameStatus.jumpExitFl) {
// Get rid of iconbar if necessary
if (gameStatus.inventoryState != I_OFF)
gameStatus.inventoryState = I_UP;
- _vm.scheduler().insertActionList(_vm._hotspots[i].actIndex);
+ _vm->_scheduler->insertActionList(_vm->_hotspots[i].actIndex);
} else { // Set up route to exit spot
- if (_vm._hotspots[i].direction == Common::KEYCODE_RIGHT)
+ if (_vm->_hotspots[i].direction == Common::KEYCODE_RIGHT)
x -= HERO_MAX_WIDTH;
- else if (_vm._hotspots[i].direction == Common::KEYCODE_LEFT)
+ else if (_vm->_hotspots[i].direction == Common::KEYCODE_LEFT)
x += HERO_MAX_WIDTH;
- if (!_vm.route().startRoute(GO_EXIT, i, x, y))
- Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ if (!_vm->_route->startRoute(GO_EXIT, i, x, y))
+ Utils::Box(BOX_ANY, "%s", _vm->_textMouse[kMsNoWayText]); // Can't get there
}
// Get rid of any attached icon
@@ -191,29 +199,29 @@ void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) {
}
break;
default: // Look at an icon or object
- obj = &_vm._objects[objId];
+ obj = &_vm->_object->_objects[objId];
// Over iconbar - immediate description
if (gameStatus.inventoryState == I_ACTIVE && cy < INV_DY + DIBOFF_Y)
- _vm.lookObject(obj);
+ _vm->_object->lookObject(obj);
else {
bool foundFl = false; // TRUE if route found to object
switch (obj->viewx) { // Clicked over viewport object
case -1: // Walk to object position
- if (_vm.findObjectSpace(obj, &x, &y))
- foundFl = _vm.route().startRoute(GO_LOOK, objId, x, y);
+ if (_vm->_object->findObjectSpace(obj, &x, &y))
+ foundFl = _vm->_route->startRoute(GO_LOOK, objId, x, y);
if (!foundFl) // Can't get there, immediate description
- _vm.lookObject(obj);
+ _vm->_object->lookObject(obj);
break;
case 0: // Immediate description
- _vm.lookObject(obj);
+ _vm->_object->lookObject(obj);
break;
default: // Walk to view point if possible
- if (!_vm.route().startRoute(GO_LOOK, objId, obj->viewx, obj->viewy)) {
- if (_vm._hero->cycling == INVISIBLE) // If invisible do
- _vm.lookObject(obj); // immediate decription
+ if (!_vm->_route->startRoute(GO_LOOK, objId, obj->viewx, obj->viewy)) {
+ if (_vm->_hero->cycling == INVISIBLE) // If invisible do
+ _vm->_object->lookObject(obj); // immediate decription
else
- Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ Utils::Box(BOX_ANY, "%s", _vm->_textMouse[kMsNoWayText]); // Can't get there
}
break;
}
@@ -222,14 +230,16 @@ void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) {
}
}
-// Process mouse activity
+/**
+* Process mouse activity
+*/
void MouseHandler::mouseHandler() {
debugC(2, kDebugMouse, "mouseHandler");
- int16 cx = _vm.getMouseX();
- int16 cy = _vm.getMouseY();
+ int16 cx = _vm->getMouseX();
+ int16 cy = _vm->getMouseY();
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
gameStatus.cx = cx; // Save cursor coords
gameStatus.cy = cy;
@@ -242,8 +252,8 @@ void MouseHandler::mouseHandler() {
if (gameStatus.inventoryObjId != -1) {
// Find index of icon
int16 iconId; // Find index of dragged icon
- for (iconId = 0; iconId < _vm._maxInvent; iconId++) {
- if (gameStatus.inventoryObjId == _vm._invent[iconId])
+ for (iconId = 0; iconId < _vm->_maxInvent; iconId++) {
+ if (gameStatus.inventoryObjId == _vm->_invent[iconId])
break;
}
@@ -260,20 +270,20 @@ void MouseHandler::mouseHandler() {
icony = MIN(icony, YPIX - INV_DY);
// Copy the icon and add to display list
- _vm.screen().moveImage(_vm.screen().getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm.screen().getFrontBuffer(), iconx, icony, XPIX);
- _vm.screen().displayList(D_ADD, iconx, icony, INV_DX, INV_DY);
+ _vm->_screen->moveImage(_vm->_screen->getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm->_screen->getFrontBuffer(), iconx, icony, XPIX);
+ _vm->_screen->displayList(D_ADD, iconx, icony, INV_DX, INV_DY);
}
int16 objId = -1; // Current source object
// Process cursor over an object or icon
if (gameStatus.inventoryState == I_ACTIVE) // Check inventory icon bar first
- objId = _vm.inventory().processInventory(INV_GET, cx, cy);
+ objId = _vm->_inventory->processInventory(INV_GET, cx, cy);
if (objId == -1) // No match, check rest of view
- objId = _vm.findObject(cx, cy);
+ objId = _vm->_object->findObject(cx, cy);
if (objId >= 0) { // Got a match
// Display object name next to cursor (unless CURSOR_NOCHAR)
// Note test for swapped hero name
- char *name = _vm._arrayNouns[_vm._objects[(objId == HERO) ? _vm._heroImage : objId].nounIndex][CURSOR_NAME];
+ char *name = _vm->_arrayNouns[_vm->_object->_objects[(objId == HERO) ? _vm->_heroImage : objId].nounIndex][CURSOR_NAME];
if (name[0] != CURSOR_NOCHAR)
cursorText(name, cx, cy, U_FONT8, _TBRIGHTWHITE);
@@ -285,9 +295,9 @@ void MouseHandler::mouseHandler() {
// Process cursor over an exit hotspot
if (objId == -1) {
int i = findExit(cx, cy);
- if (i != -1 && _vm._hotspots[i].viewx >= 0) {
+ if (i != -1 && _vm->_hotspots[i].viewx >= 0) {
objId = EXIT_HOTSPOT;
- cursorText(_vm._textMouse[kMsExit], cx, cy, U_FONT8, _TBRIGHTWHITE);
+ cursorText(_vm->_textMouse[kMsExit], cx, cy, U_FONT8, _TBRIGHTWHITE);
}
}
diff --git a/engines/hugo/mouse.h b/engines/hugo/mouse.h
index 3ac5f19f32..0fcb651b3a 100644
--- a/engines/hugo/mouse.h
+++ b/engines/hugo/mouse.h
@@ -36,12 +36,12 @@ namespace Hugo {
class MouseHandler {
public:
- MouseHandler(HugoEngine &vm);
+ MouseHandler(HugoEngine *vm);
void mouseHandler();
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
void cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color);
int16 findExit(int16 cx, int16 cy);
diff --git a/engines/hugo/object.cpp b/engines/hugo/object.cpp
new file mode 100644
index 0000000000..15f5e4fe06
--- /dev/null
+++ b/engines/hugo/object.cpp
@@ -0,0 +1,577 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler::ObjectHandler(HugoEngine *vm) : _vm(vm), _objects(0) {
+ _numObj = 0;
+}
+
+ObjectHandler::~ObjectHandler() {
+}
+
+/**
+* Save sequence number and image number in given object
+*/
+void ObjectHandler::saveSeq(object_t *obj) {
+ debugC(1, kDebugObject, "saveSeq");
+
+ bool found = false;
+ for (int i = 0; !found && (i < obj->seqNumb); i++) {
+ seq_t *q = obj->seqList[i].seqPtr;
+ for (int j = 0; !found && (j < obj->seqList[i].imageNbr); j++) {
+ if (obj->currImagePtr == q) {
+ found = true;
+ obj->curSeqNum = i;
+ obj->curImageNum = j;
+ } else {
+ q = q->nextSeqPtr;
+ }
+ }
+ }
+}
+
+/**
+* Set up cur_seq_p from stored sequence and image number in object
+*/
+void ObjectHandler::restoreSeq(object_t *obj) {
+ debugC(1, kDebugObject, "restoreSeq");
+
+ seq_t *q = obj->seqList[obj->curSeqNum].seqPtr;
+ for (int j = 0; j < obj->curImageNum; j++)
+ q = q->nextSeqPtr;
+ obj->currImagePtr = q;
+}
+
+/**
+* If status.objid = -1, pick up objid, else use status.objid on objid,
+* if objid can't be picked up, use it directly
+*/
+void ObjectHandler::useObject(int16 objId) {
+ debugC(1, kDebugObject, "useObject(%d)", objId);
+
+ char *verb; // Background verb to use directly
+ object_t *obj = &_objects[objId]; // Ptr to object
+ if (_vm->getGameStatus().inventoryObjId == -1) {
+ // Get or use objid directly
+ if ((obj->genericCmd & TAKE) || obj->objValue) // Get collectible item
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_take][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if (obj->genericCmd & LOOK) // Look item
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_look][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if (obj->genericCmd & DROP) // Drop item
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_drop][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if (obj->cmdIndex != 0) // Use non-collectible item if able
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_cmdList[obj->cmdIndex][1].verbIndex][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if ((verb = _vm->useBG(_vm->_arrayNouns[obj->nounIndex][0])) != 0)
+ sprintf(_line, "%s %s", verb, _vm->_arrayNouns[obj->nounIndex][0]);
+ else
+ return; // Can't use object directly
+ } else {
+ // Use status.objid on objid
+ // Default to first cmd verb
+ sprintf(_line, "%s %s %s", _vm->_arrayVerbs[_vm->_cmdList[_objects[_vm->getGameStatus().inventoryObjId].cmdIndex][1].verbIndex][0],
+ _vm->_arrayNouns[_objects[_vm->getGameStatus().inventoryObjId].nounIndex][0],
+ _vm->_arrayNouns[obj->nounIndex][0]);
+
+ // Check valid use of objects and override verb if necessary
+ for (uses_t *use = _vm->_uses; use->objId != _numObj; use++) {
+ if (_vm->getGameStatus().inventoryObjId == use->objId) {
+ // Look for secondary object, if found use matching verb
+ bool foundFl = false;
+ for (target_t *target = use->targets; _vm->_arrayNouns[target->nounIndex] != 0; target++)
+ if (_vm->_arrayNouns[target->nounIndex][0] == _vm->_arrayNouns[obj->nounIndex][0]) {
+ foundFl = true;
+ sprintf(_line, "%s %s %s", _vm->_arrayVerbs[target->verbIndex][0],
+ _vm->_arrayNouns[_objects[_vm->getGameStatus().inventoryObjId].nounIndex][0],
+ _vm->_arrayNouns[obj->nounIndex][0]);
+ }
+
+ // No valid use of objects found, print failure string
+ if (!foundFl) {
+ // Deselect dragged icon if inventory not active
+ if (_vm->getGameStatus().inventoryState != I_ACTIVE)
+ _vm->getGameStatus().inventoryObjId = -1;
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[use->dataIndex]);
+ return;
+ }
+ }
+ }
+ }
+
+ if (_vm->getGameStatus().inventoryState == I_ACTIVE) // If inventory active, remove it
+ _vm->getGameStatus().inventoryState = I_UP;
+ _vm->getGameStatus().inventoryObjId = -1; // Deselect any dragged icon
+ _vm->_parser->lineHandler(); // and process command
+}
+
+/**
+* Return object index of the topmost object under the cursor, or -1 if none
+* Objects are filtered if not "useful"
+*/
+int16 ObjectHandler::findObject(uint16 x, uint16 y) {
+ debugC(3, kDebugObject, "findObject(%d, %d)", x, y);
+
+ int16 objIndex = -1; // Index of found object
+ uint16 y2Max = 0; // Greatest y2
+ object_t *obj = _objects;
+ // Check objects on screen
+ for (int i = 0; i < _numObj; i++, obj++) {
+ // Object must be in current screen and "useful"
+ if (obj->screenIndex == *_vm->_screen_p && (obj->genericCmd || obj->objValue || obj->cmdIndex)) {
+ seq_t *curImage = obj->currImagePtr;
+ // Object must have a visible image...
+ if (curImage != 0 && obj->cycling != INVISIBLE) {
+ // If cursor inside object
+ if (x >= (uint16)obj->x && x <= obj->x + curImage->x2 && y >= (uint16)obj->y && y <= obj->y + curImage->y2) {
+ // If object is closest so far
+ if (obj->y + curImage->y2 > y2Max) {
+ y2Max = obj->y + curImage->y2;
+ objIndex = i; // Found an object!
+ }
+ }
+ } else {
+ // ...or a dummy object that has a hotspot rectangle
+ if (curImage == 0 && obj->vxPath != 0 && !obj->carriedFl) {
+ // If cursor inside special rectangle
+ if ((int16)x >= obj->oldx && (int16)x < obj->oldx + obj->vxPath && (int16)y >= obj->oldy && (int16)y < obj->oldy + obj->vyPath) {
+ // If object is closest so far
+ if (obj->oldy + obj->vyPath - 1 > (int16)y2Max) {
+ y2Max = obj->oldy + obj->vyPath - 1;
+ objIndex = i; // Found an object!
+ }
+ }
+ }
+ }
+ }
+ }
+ return objIndex;
+}
+
+/**
+* Issue "Look at <object>" command
+* Note special case of swapped hero image
+*/
+void ObjectHandler::lookObject(object_t *obj) {
+ debugC(1, kDebugObject, "lookObject");
+
+ if (obj == _vm->_hero)
+ // Hero swapped - look at other
+ obj = &_objects[_vm->_heroImage];
+
+ _vm->_parser->command("%s %s", _vm->_arrayVerbs[_vm->_look][0], _vm->_arrayNouns[obj->nounIndex][0]);
+}
+
+/**
+* Free all object images
+*/
+void ObjectHandler::freeObjects() {
+ debugC(1, kDebugObject, "freeObjects");
+
+ // Nothing to do if not allocated yet
+ if (_vm->_hero->seqList[0].seqPtr == 0)
+ return;
+
+ // Free all sequence lists and image data
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i];
+ for (int j = 0; j < obj->seqNumb; j++) { // for each sequence
+ seq_t *seq = obj->seqList[j].seqPtr; // Free image
+ if (obj->currImagePtr!= 0)
+ free(obj->currImagePtr);
+ if (seq == 0) // Failure during database load
+ break;
+ do {
+ free(seq->imagePtr);
+ seq = seq->nextSeqPtr;
+ } while (seq != obj->seqList[j].seqPtr);
+ free(seq); // Free sequence record
+ }
+ }
+}
+
+/**
+* Compare function for the quicksort. The sort is to order the objects in
+* increasing vertical position, using y+y2 as the baseline
+* Returns -1 if ay2 < by2 else 1 if ay2 > by2 else 0
+*/
+int ObjectHandler::y2comp(const void *a, const void *b) {
+ debugC(6, kDebugObject, "y2comp");
+
+ const object_t *p1 = &HugoEngine::get()._object->_objects[*(const byte *)a];
+ const object_t *p2 = &HugoEngine::get()._object->_objects[*(const byte *)b];
+
+ if (p1 == p2)
+ // Why does qsort try the same indexes?
+ return 0;
+
+ if (p1->priority == BACKGROUND)
+ return -1;
+
+ if (p2->priority == BACKGROUND)
+ return 1;
+
+ if (p1->priority == FOREGROUND)
+ return 1;
+
+ if (p2->priority == FOREGROUND)
+ return -1;
+
+ int ay2 = p1->y + p1->currImagePtr->y2;
+ int by2 = p2->y + p2->currImagePtr->y2;
+
+ return ay2 - by2;
+}
+
+/**
+* Return TRUE if object being carried by hero
+*/
+bool ObjectHandler::isCarrying(uint16 wordIndex) {
+ debugC(1, kDebugObject, "isCarrying(%d)", wordIndex);
+
+ for (int i = 0; i < _numObj; i++) {
+ if ((wordIndex == _objects[i].nounIndex) && _objects[i].carriedFl)
+ return true;
+ }
+ return false;
+}
+
+/**
+* Describe any takeable objects visible in this screen
+*/
+void ObjectHandler::showTakeables() {
+ debugC(1, kDebugObject, "showTakeables");
+
+ for (int j = 0; j < _numObj; j++) {
+ object_t *obj = &_objects[j];
+ if ((obj->cycling != INVISIBLE) &&
+ (obj->screenIndex == *_vm->_screen_p) &&
+ (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) {
+ Utils::Box(BOX_ANY, "You can also see:\n%s.", _vm->_arrayNouns[obj->nounIndex][LOOK_NAME]);
+ }
+ }
+}
+
+/**
+* Find a clear space around supplied object that hero can walk to
+*/
+bool ObjectHandler::findObjectSpace(object_t *obj, int16 *destx, int16 *desty) {
+ debugC(1, kDebugObject, "findObjectSpace(obj, %d, %d)", *destx, *desty);
+
+ seq_t *curImage = obj->currImagePtr;
+ int16 y = obj->y + curImage->y2 - 1;
+
+ bool foundFl = true;
+ // Try left rear corner
+ for (int16 x = *destx = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+
+ if (!foundFl) { // Try right rear corner
+ foundFl = true;
+ for (int16 x = *destx = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+ }
+
+ if (!foundFl) { // Try left front corner
+ foundFl = true;
+ y += 2;
+ for (int16 x = *destx = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+ }
+
+ if (!foundFl) { // Try right rear corner
+ foundFl = true;
+ for (int16 x = *destx = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+ }
+
+ *desty = y;
+ return foundFl;
+}
+
+/**
+* Free ObjectArr (before exiting)
+*/
+void ObjectHandler::freeObjectArr() {
+ free(_objects);
+}
+
+/**
+* Load ObjectArr from Hugo.dat
+*/
+void ObjectHandler::loadObjectArr(Common::File &in) {
+ debugC(6, kDebugObject, "loadObject(&in)");
+
+// TODO: For Hugo3, if not in story mode, set _objects[2].state to 3
+ for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
+ uint16 numElem = in.readUint16BE();
+ if (varnt == _vm->_gameVariant) {
+ _objCount = numElem;
+ _objects = (object_t *)malloc(sizeof(object_t) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ _objects[i].nounIndex = in.readUint16BE();
+ _objects[i].dataIndex = in.readUint16BE();
+ uint16 numSubElem = in.readUint16BE();
+ if (numSubElem == 0)
+ _objects[i].stateDataIndex = 0;
+ else
+ _objects[i].stateDataIndex = (uint16 *)malloc(sizeof(uint16) * numSubElem);
+ for (int j = 0; j < numSubElem; j++)
+ _objects[i].stateDataIndex[j] = in.readUint16BE();
+ _objects[i].pathType = (path_t) in.readSint16BE();
+ _objects[i].vxPath = in.readSint16BE();
+ _objects[i].vyPath = in.readSint16BE();
+ _objects[i].actIndex = in.readUint16BE();
+ _objects[i].seqNumb = in.readByte();
+ _objects[i].currImagePtr = 0;
+ if (_objects[i].seqNumb == 0) {
+ _objects[i].seqList[0].imageNbr = 0;
+ _objects[i].seqList[0].seqPtr = 0;
+ }
+ for (int j = 0; j < _objects[i].seqNumb; j++) {
+ _objects[i].seqList[j].imageNbr = in.readUint16BE();
+ _objects[i].seqList[j].seqPtr = 0;
+ }
+ _objects[i].cycling = (cycle_t)in.readByte();
+ _objects[i].cycleNumb = in.readByte();
+ _objects[i].frameInterval = in.readByte();
+ _objects[i].frameTimer = in.readByte();
+ _objects[i].radius = in.readByte();
+ _objects[i].screenIndex = in.readByte();
+ _objects[i].x = in.readSint16BE();
+ _objects[i].y = in.readSint16BE();
+ _objects[i].oldx = in.readSint16BE();
+ _objects[i].oldy = in.readSint16BE();
+ _objects[i].vx = in.readByte();
+ _objects[i].vy = in.readByte();
+ _objects[i].objValue = in.readByte();
+ _objects[i].genericCmd = in.readSint16BE();
+ _objects[i].cmdIndex = in.readUint16BE();
+ _objects[i].carriedFl = (in.readByte() != 0);
+ _objects[i].state = in.readByte();
+ _objects[i].verbOnlyFl = (in.readByte() != 0);
+ _objects[i].priority = in.readByte();
+ _objects[i].viewx = in.readSint16BE();
+ _objects[i].viewy = in.readSint16BE();
+ _objects[i].direction = in.readSint16BE();
+ _objects[i].curSeqNum = in.readByte();
+ _objects[i].curImageNum = in.readByte();
+ _objects[i].oldvx = in.readByte();
+ _objects[i].oldvy = in.readByte();
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ in.readUint16BE();
+ in.readUint16BE();
+ uint16 numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++)
+ in.readUint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ numSubElem = in.readByte();
+ for (int j = 0; j < numSubElem; j++)
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ }
+ }
+ }
+}
+
+/**
+* Set the screenindex property of the carried objets to the given screen
+* number
+*/
+void ObjectHandler::setCarriedScreen(int screenNum) {
+ for (int i = HERO + 1; i < _numObj; i++) { // Any others
+ if (isCarried(i)) // being carried
+ _objects[i].screenIndex = screenNum;
+ }
+}
+
+/**
+* Load _numObj from Hugo.dat
+*/
+void ObjectHandler::loadNumObj(Common::File &in) {
+ int numElem;
+
+ for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _vm->_gameVariant)
+ _numObj = numElem;
+ }
+}
+
+/**
+* Restore all sequences
+*/
+void ObjectHandler::restoreAllSeq() {
+ // Restore ptrs to currently loaded objects
+ for (int i = 0; i < _numObj; i++)
+ restoreSeq(&_objects[i]);
+}
+
+/**
+* Save objects
+*/
+void ObjectHandler::saveObjects(Common::WriteStream *out) {
+ for (int i = 0; i < _numObj; i++) {
+ // Save where curr_seq_p is pointing to
+ saveSeq(&_objects[i]);
+
+ out->writeByte(_objects[i].pathType);
+ out->writeSint16BE(_objects[i].vxPath);
+ out->writeSint16BE(_objects[i].vyPath);
+ out->writeByte(_objects[i].cycling);
+ out->writeByte(_objects[i].cycleNumb);
+ out->writeByte(_objects[i].frameTimer);
+ out->writeByte(_objects[i].screenIndex);
+ out->writeSint16BE(_objects[i].x);
+ out->writeSint16BE(_objects[i].y);
+ out->writeSint16BE(_objects[i].oldx);
+ out->writeSint16BE(_objects[i].oldy);
+ out->writeSByte(_objects[i].vx);
+ out->writeSByte(_objects[i].vy);
+ out->writeByte(_objects[i].objValue);
+ out->writeByte((_objects[i].carriedFl) ? 1 : 0);
+ out->writeByte(_objects[i].state);
+ out->writeByte(_objects[i].priority);
+ out->writeSint16BE(_objects[i].viewx);
+ out->writeSint16BE(_objects[i].viewy);
+ out->writeSint16BE(_objects[i].direction);
+ out->writeByte(_objects[i].curSeqNum);
+ out->writeByte(_objects[i].curImageNum);
+ out->writeSByte(_objects[i].oldvx);
+ out->writeSByte(_objects[i].oldvy);
+ }
+}
+
+/**
+* Restore objects
+*/
+void ObjectHandler::restoreObjects(Common::SeekableReadStream *in) {
+ for (int i = 0; i < _numObj; i++) {
+ _objects[i].pathType = (path_t) in->readByte();
+ _objects[i].vxPath = in->readSint16BE();
+ _objects[i].vyPath = in->readSint16BE();
+ _objects[i].cycling = (cycle_t) in->readByte();
+ _objects[i].cycleNumb = in->readByte();
+ _objects[i].frameTimer = in->readByte();
+ _objects[i].screenIndex = in->readByte();
+ _objects[i].x = in->readSint16BE();
+ _objects[i].y = in->readSint16BE();
+ _objects[i].oldx = in->readSint16BE();
+ _objects[i].oldy = in->readSint16BE();
+ _objects[i].vx = in->readSByte();
+ _objects[i].vy = in->readSByte();
+ _objects[i].objValue = in->readByte();
+ _objects[i].carriedFl = (in->readByte() == 1);
+ _objects[i].state = in->readByte();
+ _objects[i].priority = in->readByte();
+ _objects[i].viewx = in->readSint16BE();
+ _objects[i].viewy = in->readSint16BE();
+ _objects[i].direction = in->readSint16BE();
+ _objects[i].curSeqNum = in->readByte();
+ _objects[i].curImageNum = in->readByte();
+ _objects[i].oldvx = in->readSByte();
+ _objects[i].oldvy = in->readSByte();
+ }
+}
+
+/**
+* Compute max object score
+*/
+int ObjectHandler::calcMaxScore() {
+ int score = 0;
+ for (int i = 0; i < _numObj; i++)
+ score += _objects[i].objValue;
+ return(score);
+}
+
+/**
+* Read Object images
+*/
+void ObjectHandler::readObjectImages() {
+ debugC(1, kDebugObject, "readObjectImages");
+
+ for (int i = 0; i < _numObj; i++)
+ _vm->_file->readImage(i, &_objects[i]);
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object.h b/engines/hugo/object.h
new file mode 100644
index 0000000000..c8e1be7f75
--- /dev/null
+++ b/engines/hugo/object.h
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_OBJECT_H
+#define HUGO_OBJECT_H
+
+#include "common/file.h"
+
+#define MAXOBJECTS 128 // Used in Update_images()
+#define BOUND(X, Y) ((_vm->getBoundaryOverlay()[Y * XBYTES + X / 8] & (0x80 >> X % 8)) != 0) // Boundary bit set
+
+namespace Hugo {
+
+class ObjectHandler {
+public:
+ ObjectHandler(HugoEngine *vm);
+ virtual ~ObjectHandler();
+
+ object_t *_objects;
+ uint16 _numObj;
+
+ virtual void moveObjects() = 0;
+ virtual void updateImages() = 0;
+ virtual void swapImages(int objNumb1, int objNumb2) = 0;
+
+ bool isCarrying(uint16 wordIndex);
+ bool findObjectSpace(object_t *obj, int16 *destx, int16 *desty);
+
+ int calcMaxScore();
+ int16 findObject(uint16 x, uint16 y);
+ void freeObjects();
+ void loadObjectArr(Common::File &in);
+ void freeObjectArr();
+ void loadNumObj(Common::File &in);
+ void lookObject(object_t *obj);
+ void readObjectImages();
+ void restoreAllSeq();
+ void restoreObjects(Common::SeekableReadStream *in);
+ void saveObjects(Common::WriteStream *out);
+ void saveSeq(object_t *obj);
+ void setCarriedScreen(int screenNum);
+ void showTakeables();
+ void useObject(int16 objId);
+
+ static int y2comp(const void *a, const void *b);
+
+ bool isCarried(int objIndex) {
+ return _objects[objIndex].carriedFl;
+ }
+
+ void setCarry(int objIndex, bool val) {
+ _objects[objIndex].carriedFl = val;
+ }
+
+ void setVelocity(int objIndex, int8 vx, int8 vy) {
+ _objects[objIndex].vx = vx;
+ _objects[objIndex].vy = vy;
+ }
+
+ void setPath(int objIndex, path_t pathType, int16 vxPath, int16 vyPath) {
+ _objects[objIndex].pathType = pathType;
+ _objects[objIndex].vxPath = vxPath;
+ _objects[objIndex].vyPath = vyPath;
+ }
+protected:
+ HugoEngine *_vm;
+ uint16 _objCount;
+
+ void restoreSeq(object_t *obj);
+};
+
+class ObjectHandler_v1d : public ObjectHandler {
+public:
+ ObjectHandler_v1d(HugoEngine *vm);
+ virtual ~ObjectHandler_v1d();
+
+ void moveObjects();
+ void updateImages();
+ void swapImages(int objNumb1, int objNumb2);
+};
+
+class ObjectHandler_v1w : public ObjectHandler {
+public:
+ ObjectHandler_v1w(HugoEngine *vm);
+ ~ObjectHandler_v1w();
+
+ void moveObjects();
+ void updateImages();
+ void swapImages(int objNumb1, int objNumb2);
+};
+
+class ObjectHandler_v2d : public ObjectHandler_v1d {
+public:
+ ObjectHandler_v2d(HugoEngine *vm);
+ virtual ~ObjectHandler_v2d();
+
+ void moveObjects();
+ void updateImages();
+};
+
+class ObjectHandler_v3d : public ObjectHandler_v2d {
+public:
+ ObjectHandler_v3d(HugoEngine *vm);
+ ~ObjectHandler_v3d();
+
+ void moveObjects();
+ void swapImages(int objNumb1, int objNumb2);
+};
+
+} // End of namespace Hugo
+#endif //HUGO_OBJECT_H
diff --git a/engines/hugo/object_v1d.cpp b/engines/hugo/object_v1d.cpp
new file mode 100644
index 0000000000..669c4fb131
--- /dev/null
+++ b/engines/hugo/object_v1d.cpp
@@ -0,0 +1,372 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler_v1d::ObjectHandler_v1d(HugoEngine *vm) : ObjectHandler(vm) {
+}
+
+ObjectHandler_v1d::~ObjectHandler_v1d() {
+}
+
+/**
+* Draw all objects on screen as follows:
+* 1. Sort 'FLOATING' objects in order of y2 (base of object)
+* 2. Display new object frames/positions in dib
+* Finally, cycle any animating objects to next frame
+*/
+void ObjectHandler_v1d::updateImages() {
+ debugC(5, kDebugObject, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAXOBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling >= ALMOST_INVISIBLE))
+ objindex[num_objs++] = i;
+ }
+
+ // Sort the objects into increasing y+y2 (painter's algorithm)
+ qsort(objindex, num_objs, sizeof(objindex[0]), y2comp);
+
+ // Add each visible object to display list
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ // Count down inter-frame timer
+ if (obj->frameTimer)
+ obj->frameTimer--;
+
+ if (obj->cycling > ALMOST_INVISIBLE) { // Only if visible
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, false);
+ break;
+ case CYCLE_FORWARD:
+ if (obj->frameTimer) // Not time to see next frame yet
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, false);
+ else
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, false);
+ break;
+ case CYCLE_BACKWARD: {
+ seq_t *seqPtr = obj->currImagePtr;
+ if (!obj->frameTimer) { // Show next frame
+ while (seqPtr->nextSeqPtr != obj->currImagePtr)
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+ _vm->_screen->displayFrame(obj->x, obj->y, seqPtr, false);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ _vm->_scheduler->waitForRefresh();
+
+ // Cycle any animating objects
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ if (obj->cycling != INVISIBLE) {
+ // Only if it's visible
+ if (obj->cycling == ALMOST_INVISIBLE)
+ obj->cycling = INVISIBLE;
+
+ // Now Rotate to next picture in sequence
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ break;
+ case CYCLE_FORWARD:
+ if (!obj->frameTimer) {
+ // Time to step to next frame
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is last frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb) { // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CYCLE_BACKWARD: {
+ if (!obj->frameTimer) {
+ // Time to step to prev frame
+ seq_t *seqPtr = obj->currImagePtr;
+ while (obj->currImagePtr->nextSeqPtr != seqPtr)
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is first frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb){ // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ obj->oldx = obj->x;
+ obj->oldy = obj->y;
+ }
+ }
+}
+
+/**
+* Update all object positions. Process object 'local' events
+* including boundary events and collisions
+*/
+void ObjectHandler_v1d::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ static int dxOld, dyOld; // previous directions for CHASEing
+
+ // Added to DOS version in order to handle mouse properly
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_route->processRoute();
+
+ // Perform any adjustments to velocity based on special path types
+ // and store all (visible) object baselines into the boundary file.
+ // Don't store foreground or background objects
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_screen_p) {
+ switch (obj->pathType) {
+ case CHASE: {
+ // Allowable motion wrt boundary
+ int dx = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+ if (abs(dx) <= 1)
+ obj->vx = 0;
+ else
+ obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+ if (abs(dy) <= 1)
+ obj->vy = 0;
+ else
+ obj->vy = (dy > 0) ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
+
+ // Set first image in sequence (if multi-seq object)
+ if (obj->seqNumb == 4) {
+ if (!obj->vx) { // Got 4 directions
+ if (obj->vx != dxOld) { // vx just stopped
+ if (dy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != dxOld) {
+ if (dx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ }
+
+ if (obj->vx || obj->vy) {
+ if (obj->seqNumb > 1)
+ obj->cycling = CYCLE_FORWARD;
+ } else {
+ obj->cycling = NOT_CYCLING;
+ _vm->boundaryCollision(obj); // Must have got hero!
+ }
+ dxOld = obj->vx;
+ dyOld = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ break;
+ }
+ case WANDER:
+ if (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
+
+ // Set first image in sequence (if multi-seq object)
+ if (obj->seqNumb > 1) {
+ if (!obj->vx && (obj->seqNumb > 2)) {
+ if (obj->vx != dxOld) { // vx just stopped
+ if (obj->vy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != dxOld) {
+ if (obj->vx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ else
+ obj->cycling = NOT_CYCLING;
+ }
+ dxOld = obj->vx;
+ dyOld = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ }
+ break;
+ default:
+ ; // Really, nothing
+ }
+ // Store boundaries
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->vx || obj->vy)) {
+ // Only process if it's moving
+
+ // Do object movement. Delta_x,y return allowed movement in x,y
+ // to move as close to a boundary as possible without crossing it.
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ // object coordinates
+ int x1 = obj->x + currImage->x1; // Left edge of object
+ int x2 = obj->x + currImage->x2; // Right edge
+ int y1 = obj->y + currImage->y1; // Top edge
+ int y2 = obj->y + currImage->y2; // Bottom edge
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(x1, x2, y2); // Re-store our own boundary
+
+ obj->x += dx; // Update object position
+ obj->y += dy;
+
+ // Don't let object go outside screen
+ if (x1 < EDGE)
+ obj->x = EDGE2;
+ if (x2 > (XPIX - EDGE))
+ obj->x = XPIX - EDGE2 - (x2 - x1);
+ if (y1 < EDGE)
+ obj->y = EDGE2;
+ if (y2 > (YPIX - EDGE))
+ obj->y = YPIX - EDGE2 - (y2 - y1);
+
+ if ((obj->vx == 0) && (obj->vy == 0))
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+
+ // Clear all object baselines from the boundary file.
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
+ }
+
+ // If maze mode is enabled, do special maze processing
+ if (_maze.enabledFl) {
+ seq_t *currImage = _vm->_hero->currImagePtr; // Get ptr to current image
+ // hero coordinates
+ int x1 = _vm->_hero->x + currImage->x1; // Left edge of object
+ int x2 = _vm->_hero->x + currImage->x2; // Right edge
+ int y1 = _vm->_hero->y + currImage->y1; // Top edge
+ int y2 = _vm->_hero->y + currImage->y2; // Bottom edge
+
+ _vm->_scheduler->processMaze(x1, x2, y1, y2);
+ }
+}
+
+/**
+* Swap all the images of one object with another. Set hero_image (we make
+* the assumption for now that the first obj is always the HERO) to the object
+* number of the swapped image
+*/
+void ObjectHandler_v1d::swapImages(int objNumb1, int objNumb2) {
+ debugC(1, kDebugObject, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _objects[objNumb1].seqList, seqListSize);
+ memcpy(_objects[objNumb1].seqList, _objects[objNumb2].seqList, seqListSize);
+ memcpy(_objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ _objects[objNumb1].currImagePtr = _objects[objNumb1].seqList[0].seqPtr;
+ _objects[objNumb2].currImagePtr = _objects[objNumb2].seqList[0].seqPtr;
+ _vm->_heroImage = (_vm->_heroImage == HERO) ? objNumb2 : HERO;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object_v1w.cpp b/engines/hugo/object_v1w.cpp
new file mode 100644
index 0000000000..b47bd0e488
--- /dev/null
+++ b/engines/hugo/object_v1w.cpp
@@ -0,0 +1,385 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler_v1w::ObjectHandler_v1w(HugoEngine *vm) : ObjectHandler(vm) {
+}
+
+ObjectHandler_v1w::~ObjectHandler_v1w() {
+}
+
+/**
+* Draw all objects on screen as follows:
+* 1. Sort 'FLOATING' objects in order of y2 (base of object)
+* 2. Display new object frames/positions in dib
+* Finally, cycle any animating objects to next frame
+*/
+void ObjectHandler_v1w::updateImages() {
+ debugC(5, kDebugObject, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAXOBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling >= ALMOST_INVISIBLE))
+ objindex[num_objs++] = i;
+ }
+
+ // Sort the objects into increasing y+y2 (painter's algorithm)
+ qsort(objindex, num_objs, sizeof(objindex[0]), y2comp);
+
+ // Add each visible object to display list
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ // Count down inter-frame timer
+ if (obj->frameTimer)
+ obj->frameTimer--;
+
+ if (obj->cycling > ALMOST_INVISIBLE) { // Only if visible
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ break;
+ case CYCLE_FORWARD:
+ if (obj->frameTimer) // Not time to see next frame yet
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ else
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, obj->priority == OVEROVL);
+ break;
+ case CYCLE_BACKWARD: {
+ seq_t *seqPtr = obj->currImagePtr;
+ if (!obj->frameTimer) { // Show next frame
+ while (seqPtr->nextSeqPtr != obj->currImagePtr)
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+ _vm->_screen->displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ // Cycle any animating objects
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ if (obj->cycling != INVISIBLE) {
+ // Only if it's visible
+ if (obj->cycling == ALMOST_INVISIBLE)
+ obj->cycling = INVISIBLE;
+
+ // Now Rotate to next picture in sequence
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ break;
+ case CYCLE_FORWARD:
+ if (!obj->frameTimer) {
+ // Time to step to next frame
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is last frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb) { // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CYCLE_BACKWARD: {
+ if (!obj->frameTimer) {
+ // Time to step to prev frame
+ seq_t *seqPtr = obj->currImagePtr;
+ while (obj->currImagePtr->nextSeqPtr != seqPtr)
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is first frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb){ // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ obj->oldx = obj->x;
+ obj->oldy = obj->y;
+ }
+ }
+}
+
+/**
+* Update all object positions. Process object 'local' events
+* including boundary events and collisions
+*/
+void ObjectHandler_v1w::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_route->processRoute();
+
+ // Perform any adjustments to velocity based on special path types
+ // and store all (visible) object baselines into the boundary file.
+ // Don't store foreground or background objects
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_screen_p) {
+ switch (obj->pathType) {
+ case CHASE:
+ case CHASE2: {
+ int8 radius = obj->radius; // Default to object's radius
+ if (radius < 0) // If radius infinity, use closer value
+ radius = DX;
+
+ // Allowable motion wrt boundary
+ int dx = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+ if (abs(dx) <= radius)
+ obj->vx = 0;
+ else
+ obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+ if (abs(dy) <= radius)
+ obj->vy = 0;
+ else
+ obj->vy = (dy > 0) ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
+
+ // Set first image in sequence (if multi-seq object)
+ switch (obj->seqNumb) {
+ case 4:
+ if (!obj->vx) { // Got 4 directions
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dy >= 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (dx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ case 3:
+ case 2:
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dx > 0) // Left & right only
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ }
+
+ if (obj->vx || obj->vy) {
+ obj->cycling = CYCLE_FORWARD;
+ } else {
+ obj->cycling = NOT_CYCLING;
+ _vm->boundaryCollision(obj); // Must have got hero!
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ break;
+ }
+ case WANDER2:
+ case WANDER:
+ if (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
+
+ // Set first image in sequence (if multi-seq object)
+ if (obj->seqNumb > 1) {
+ if (!obj->vx && (obj->seqNumb >= 4)) {
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (obj->vy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (obj->vx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ }
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ break;
+ default:
+ ; // Really, nothing
+ }
+ // Store boundaries
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->vx || obj->vy)) {
+ // Only process if it's moving
+
+ // Do object movement. Delta_x,y return allowed movement in x,y
+ // to move as close to a boundary as possible without crossing it.
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ // object coordinates
+ int x1 = obj->x + currImage->x1; // Left edge of object
+ int x2 = obj->x + currImage->x2; // Right edge
+ int y1 = obj->y + currImage->y1; // Top edge
+ int y2 = obj->y + currImage->y2; // Bottom edge
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(x1, x2, y2); // Re-store our own boundary
+
+ obj->x += dx; // Update object position
+ obj->y += dy;
+
+ // Don't let object go outside screen
+ if (x1 < EDGE)
+ obj->x = EDGE2;
+ if (x2 > (XPIX - EDGE))
+ obj->x = XPIX - EDGE2 - (x2 - x1);
+ if (y1 < EDGE)
+ obj->y = EDGE2;
+ if (y2 > (YPIX - EDGE))
+ obj->y = YPIX - EDGE2 - (y2 - y1);
+
+ if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2))
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+
+ // Clear all object baselines from the boundary file.
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
+ }
+
+ // If maze mode is enabled, do special maze processing
+ if (_maze.enabledFl) {
+ seq_t *currImage = _vm->_hero->currImagePtr; // Get ptr to current image
+ // hero coordinates
+ int x1 = _vm->_hero->x + currImage->x1; // Left edge of object
+ int x2 = _vm->_hero->x + currImage->x2; // Right edge
+ int y1 = _vm->_hero->y + currImage->y1; // Top edge
+ int y2 = _vm->_hero->y + currImage->y2; // Bottom edge
+
+ _vm->_scheduler->processMaze(x1, x2, y1, y2);
+ }
+}
+
+/**
+* Swap all the images of one object with another. Set hero_image (we make
+* the assumption for now that the first obj is always the HERO) to the object
+* number of the swapped image
+*/
+void ObjectHandler_v1w::swapImages(int objNumb1, int objNumb2) {
+ debugC(1, kDebugObject, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ saveSeq(&_objects[objNumb1]);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _objects[objNumb1].seqList, seqListSize);
+ memcpy(_objects[objNumb1].seqList, _objects[objNumb2].seqList, seqListSize);
+ memcpy(_objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ restoreSeq(&_objects[objNumb1]);
+ _objects[objNumb2].currImagePtr = _objects[objNumb2].seqList[0].seqPtr;
+ _vm->_heroImage = (_vm->_heroImage == HERO) ? objNumb2 : HERO;
+
+ // Make sure baseline stays constant
+ _objects[objNumb1].y += _objects[objNumb2].currImagePtr->y2 - _objects[objNumb1].currImagePtr->y2;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object_v2d.cpp b/engines/hugo/object_v2d.cpp
new file mode 100644
index 0000000000..d798a54fd1
--- /dev/null
+++ b/engines/hugo/object_v2d.cpp
@@ -0,0 +1,364 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler_v2d::ObjectHandler_v2d(HugoEngine *vm) : ObjectHandler_v1d(vm) {
+}
+
+ObjectHandler_v2d::~ObjectHandler_v2d() {
+}
+
+/**
+* Draw all objects on screen as follows:
+* 1. Sort 'FLOATING' objects in order of y2 (base of object)
+* 2. Display new object frames/positions in dib
+* Finally, cycle any animating objects to next frame
+*/
+void ObjectHandler_v2d::updateImages() {
+ debugC(5, kDebugObject, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAXOBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling >= ALMOST_INVISIBLE))
+ objindex[num_objs++] = i;
+ }
+
+ // Sort the objects into increasing y+y2 (painter's algorithm)
+ qsort(objindex, num_objs, sizeof(objindex[0]), y2comp);
+
+ // Add each visible object to display list
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ // Count down inter-frame timer
+ if (obj->frameTimer)
+ obj->frameTimer--;
+
+ if (obj->cycling > ALMOST_INVISIBLE) { // Only if visible
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ break;
+ case CYCLE_FORWARD:
+ if (obj->frameTimer) // Not time to see next frame yet
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ else
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, obj->priority == OVEROVL);
+ break;
+ case CYCLE_BACKWARD: {
+ seq_t *seqPtr = obj->currImagePtr;
+ if (!obj->frameTimer) { // Show next frame
+ while (seqPtr->nextSeqPtr != obj->currImagePtr)
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+ _vm->_screen->displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ _vm->_scheduler->waitForRefresh();
+
+ // Cycle any animating objects
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ if (obj->cycling != INVISIBLE) {
+ // Only if it's visible
+ if (obj->cycling == ALMOST_INVISIBLE)
+ obj->cycling = INVISIBLE;
+
+ // Now Rotate to next picture in sequence
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ break;
+ case CYCLE_FORWARD:
+ if (!obj->frameTimer) {
+ // Time to step to next frame
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is last frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb) { // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CYCLE_BACKWARD: {
+ if (!obj->frameTimer) {
+ // Time to step to prev frame
+ seq_t *seqPtr = obj->currImagePtr;
+ while (obj->currImagePtr->nextSeqPtr != seqPtr)
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is first frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb){ // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ obj->oldx = obj->x;
+ obj->oldy = obj->y;
+ }
+ }
+}
+
+/**
+* Update all object positions. Process object 'local' events
+* including boundary events and collisions
+*/
+void ObjectHandler_v2d::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ // Added to DOS version in order to handle mouse properly
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_route->processRoute();
+
+ // Perform any adjustments to velocity based on special path types
+ // and store all (visible) object baselines into the boundary file.
+ // Don't store foreground or background objects
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_screen_p) {
+ switch (obj->pathType) {
+ case CHASE:
+ case CHASE2: {
+ int8 radius = obj->radius; // Default to object's radius
+ if (radius < 0) // If radius infinity, use closer value
+ radius = DX;
+
+ // Allowable motion wrt boundary
+ int dx = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+ if (abs(dx) <= radius)
+ obj->vx = 0;
+ else
+ obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+ if (abs(dy) <= radius)
+ obj->vy = 0;
+ else
+ obj->vy = (dy > 0) ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
+
+ // Set first image in sequence (if multi-seq object)
+ switch (obj->seqNumb) {
+ case 4:
+ if (!obj->vx) { // Got 4 directions
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (dx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ case 3:
+ case 2:
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dx > 0) // Left & right only
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ }
+
+ if (obj->vx || obj->vy) {
+ obj->cycling = CYCLE_FORWARD;
+ } else {
+ obj->cycling = NOT_CYCLING;
+ _vm->boundaryCollision(obj); // Must have got hero!
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ break;
+ }
+ case WANDER2:
+ case WANDER:
+ if (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
+
+ // Set first image in sequence (if multi-seq object)
+ if (obj->seqNumb > 1) {
+ if (!obj->vx && (obj->seqNumb >= 4)) {
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (obj->vy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (obj->vx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ }
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ break;
+ default:
+ ; // Really, nothing
+ }
+ // Store boundaries
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->vx || obj->vy)) {
+ // Only process if it's moving
+
+ // Do object movement. Delta_x,y return allowed movement in x,y
+ // to move as close to a boundary as possible without crossing it.
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ // object coordinates
+ int x1 = obj->x + currImage->x1; // Left edge of object
+ int x2 = obj->x + currImage->x2; // Right edge
+ int y1 = obj->y + currImage->y1; // Top edge
+ int y2 = obj->y + currImage->y2; // Bottom edge
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(x1, x2, y2); // Re-store our own boundary
+
+ obj->x += dx; // Update object position
+ obj->y += dy;
+
+ // Don't let object go outside screen
+ if (x1 < EDGE)
+ obj->x = EDGE2;
+ if (x2 > (XPIX - EDGE))
+ obj->x = XPIX - EDGE2 - (x2 - x1);
+ if (y1 < EDGE)
+ obj->y = EDGE2;
+ if (y2 > (YPIX - EDGE))
+ obj->y = YPIX - EDGE2 - (y2 - y1);
+
+ if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2))
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+
+ // Clear all object baselines from the boundary file.
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
+ }
+
+ // If maze mode is enabled, do special maze processing
+ if (_maze.enabledFl) {
+ seq_t *currImage = _vm->_hero->currImagePtr; // Get ptr to current image
+ // hero coordinates
+ int x1 = _vm->_hero->x + currImage->x1; // Left edge of object
+ int x2 = _vm->_hero->x + currImage->x2; // Right edge
+ int y1 = _vm->_hero->y + currImage->y1; // Top edge
+ int y2 = _vm->_hero->y + currImage->y2; // Bottom edge
+
+ _vm->_scheduler->processMaze(x1, x2, y1, y2);
+ }
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object_v3d.cpp b/engines/hugo/object_v3d.cpp
new file mode 100644
index 0000000000..ce99ef4c48
--- /dev/null
+++ b/engines/hugo/object_v3d.cpp
@@ -0,0 +1,266 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler_v3d::ObjectHandler_v3d(HugoEngine *vm) : ObjectHandler_v2d(vm) {
+}
+
+ObjectHandler_v3d::~ObjectHandler_v3d() {
+}
+
+/**
+* Update all object positions. Process object 'local' events
+* including boundary events and collisions
+*/
+void ObjectHandler_v3d::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ // Added to DOS version in order to handle mouse properly
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_route->processRoute();
+
+ // Perform any adjustments to velocity based on special path types
+ // and store all (visible) object baselines into the boundary file.
+ // Don't store foreground or background objects
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_screen_p) {
+ switch (obj->pathType) {
+ case CHASE:
+ case CHASE2: {
+ int8 radius = obj->radius; // Default to object's radius
+ if (radius < 0) // If radius infinity, use closer value
+ radius = DX;
+
+ // Allowable motion wrt boundary
+ int dx = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+ if (abs(dx) <= radius)
+ obj->vx = 0;
+ else
+ obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+ if (abs(dy) <= radius)
+ obj->vy = 0;
+ else
+ obj->vy = (dy > 0) ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
+
+ // Set first image in sequence (if multi-seq object)
+ switch (obj->seqNumb) {
+ case 4:
+ if (!obj->vx) { // Got 4 directions
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dy >= 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (dx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ case 3:
+ case 2:
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dx > 0) // Left & right only
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ }
+
+ if (obj->vx || obj->vy) {
+ obj->cycling = CYCLE_FORWARD;
+ } else {
+ obj->cycling = NOT_CYCLING;
+ _vm->boundaryCollision(obj); // Must have got hero!
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ break;
+ }
+ case WANDER2:
+ case WANDER:
+ if (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
+
+ // Set first image in sequence (if multi-seq object)
+ if (obj->seqNumb > 1) {
+ if (!obj->vx && (obj->seqNumb >= 4)) {
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (obj->vy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (obj->vx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ }
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ break;
+ default:
+ ; // Really, nothing
+ }
+ // Store boundaries
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->vx || obj->vy)) {
+ // Only process if it's moving
+
+ // Do object movement. Delta_x,y return allowed movement in x,y
+ // to move as close to a boundary as possible without crossing it.
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ // object coordinates
+ int x1 = obj->x + currImage->x1; // Left edge of object
+ int x2 = obj->x + currImage->x2; // Right edge
+ int y1 = obj->y + currImage->y1; // Top edge
+ int y2 = obj->y + currImage->y2; // Bottom edge
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(x1, x2, y2); // Re-store our own boundary
+
+ obj->x += dx; // Update object position
+ obj->y += dy;
+
+ // Don't let object go outside screen
+ if (x1 < EDGE)
+ obj->x = EDGE2;
+ if (x2 > (XPIX - EDGE))
+ obj->x = XPIX - EDGE2 - (x2 - x1);
+ if (y1 < EDGE)
+ obj->y = EDGE2;
+ if (y2 > (YPIX - EDGE))
+ obj->y = YPIX - EDGE2 - (y2 - y1);
+
+ if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2))
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+
+ // Clear all object baselines from the boundary file.
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
+ }
+
+ // If maze mode is enabled, do special maze processing
+ if (_maze.enabledFl) {
+ seq_t *currImage = _vm->_hero->currImagePtr;// Get ptr to current image
+ // hero coordinates
+ int x1 = _vm->_hero->x + currImage->x1; // Left edge of object
+ int x2 = _vm->_hero->x + currImage->x2; // Right edge
+ int y1 = _vm->_hero->y + currImage->y1; // Top edge
+ int y2 = _vm->_hero->y + currImage->y2; // Bottom edge
+
+ _vm->_scheduler->processMaze(x1, x2, y1, y2);
+ }
+}
+
+/**
+* Swap all the images of one object with another. Set hero_image (we make
+* the assumption for now that the first obj is always the HERO) to the object
+* number of the swapped image
+*/
+void ObjectHandler_v3d::swapImages(int objNumb1, int objNumb2) {
+ debugC(1, kDebugObject, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ saveSeq(&_objects[objNumb1]);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _objects[objNumb1].seqList, seqListSize);
+ memcpy(_objects[objNumb1].seqList, _objects[objNumb2].seqList, seqListSize);
+ memcpy(_objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ restoreSeq(&_objects[objNumb1]);
+ _objects[objNumb2].currImagePtr = _objects[objNumb2].seqList[0].seqPtr;
+ _vm->_heroImage = (_vm->_heroImage == HERO) ? objNumb2 : HERO;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp
index 4277d6e845..e6f88fd556 100644
--- a/engines/hugo/parser.cpp
+++ b/engines/hugo/parser.cpp
@@ -36,9 +36,11 @@
#include "hugo/parser.h"
#include "hugo/file.h"
#include "hugo/display.h"
+#include "hugo/schedule.h"
#include "hugo/route.h"
#include "hugo/util.h"
#include "hugo/sound.h"
+#include "hugo/object.h"
namespace Hugo {
@@ -46,7 +48,7 @@ namespace Hugo {
#define CX(X) LOWORD(X)
#define CY(Y) HIWORD(Y)
-Parser::Parser(HugoEngine &vm) :
+Parser::Parser(HugoEngine *vm) :
_vm(vm), _putIndex(0), _getIndex(0), _checkDoubleF1Fl(false) {
}
@@ -56,7 +58,7 @@ Parser::~Parser() {
void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
debugC(1, kDebugParser, "keyHandler(%d, %d)", nChar, nFlags);
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
bool repeatedFl = (nFlags & 0x4000); // TRUE if key is a repeat
// Process key down event - called from OnKeyDown()
@@ -74,14 +76,14 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
case Common::KEYCODE_DOWN:
if (!repeatedFl) {
gameStatus.routeIndex = -1; // Stop any automatic route
- _vm.route().setWalk(nChar); // Direction of hero travel
+ _vm->_route->setWalk(nChar); // Direction of hero travel
}
break;
case Common::KEYCODE_F1: // User Help (DOS)
if (_checkDoubleF1Fl)
- _vm.file().instructions();
+ _vm->_file->instructions();
else
- _vm.screen().userHelp();
+ _vm->_screen->userHelp();
_checkDoubleF1Fl = !_checkDoubleF1Fl;
break;
case Common::KEYCODE_F6: // Inventory
@@ -91,21 +93,29 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
_config.turboFl = !_config.turboFl;
break;
case Common::KEYCODE_F2: // Toggle sound
- _vm.sound().toggleSound();
- _vm.sound().toggleMusic();
+ _vm->_sound->toggleSound();
+ _vm->_sound->toggleMusic();
break;
case Common::KEYCODE_F3: // Repeat last line
gameStatus.recallFl = true;
break;
case Common::KEYCODE_F4: // Save game
+ // TODO: Add a proper screen to select saveslot
+ if (gameStatus.viewState == V_PLAY)
+ _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
+ break;
case Common::KEYCODE_F5: // Restore game
+ // TODO: Add a proper screen to specify saveslot and description
+ _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_scheduler->restoreScreen(*_vm->_screen_p);
+ gameStatus.viewState = V_PLAY;
+ break;
case Common::KEYCODE_F9: // Boss button
warning("STUB: KeyHandler() - F4-F5-F9 (DOS)");
break;
default: // Any other key
if (!gameStatus.storyModeFl) { // Keyboard disabled
// Add printable keys to ring buffer
-
uint16 bnext = _putIndex + 1;
if (bnext >= sizeof(_ringBuffer))
bnext = 0;
@@ -120,8 +130,10 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
_checkDoubleF1Fl = false;
}
-// Add any new chars to line buffer and display them.
-// If CR pressed, pass line to LineHandler()
+/**
+* Add any new chars to line buffer and display them.
+* If CR pressed, pass line to LineHandler()
+*/
void Parser::charHandler() {
debugC(4, kDebugParser, "charHandler");
@@ -129,7 +141,7 @@ void Parser::charHandler() {
static uint32 tick = 0; // For flashing cursor
static char cursor = '_';
static command_t cmdLine; // Build command line
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
// Check for one or more characters in ring buffer
while (_getIndex != _putIndex) {
@@ -143,7 +155,7 @@ void Parser::charHandler() {
cmdLine[--lineIndex] = '\0';
break;
case Common::KEYCODE_RETURN: // EOL, pass line to line handler
- if (lineIndex && (_vm._hero->pathType != QUIET)) {
+ if (lineIndex && (_vm->_hero->pathType != QUIET)) {
// Remove inventory bar if active
if (gameStatus.inventoryState == I_ACTIVE)
gameStatus.inventoryState = I_UP;
@@ -176,8 +188,8 @@ void Parser::charHandler() {
lineIndex = strlen(cmdLine);
}
- sprintf(_vm._statusLine, ">%s%c", cmdLine, cursor);
- sprintf(_vm._scoreLine, "F1-Help %s Score: %d of %d Sound %s", (_config.turboFl) ? "T" : " ", _vm.getScore(), _vm.getMaxScore(), (_config.soundFl) ? "On" : "Off");
+ sprintf(_vm->_statusLine, ">%s%c", cmdLine, cursor);
+ sprintf(_vm->_scoreLine, "F1-Help %s Score: %d of %d Sound %s", (_config.turboFl) ? "T" : " ", _vm->getScore(), _vm->getMaxScore(), (_config.soundFl) ? "On" : "Off");
// See if "look" button pressed
if (gameStatus.lookFl) {
@@ -186,8 +198,10 @@ void Parser::charHandler() {
}
}
-// Perform an immediate command. Takes parameters a la sprintf
-// Assumes final string will not overrun line[] length
+/**
+* Perform an immediate command. Takes parameters a la sprintf
+* Assumes final string will not overrun line[] length
+*/
void Parser::command(const char *format, ...) {
debugC(1, kDebugParser, "Command(%s, ...)", format);
@@ -199,7 +213,9 @@ void Parser::command(const char *format, ...) {
lineHandler();
}
-// Locate any member of object name list appearing in command line
+/**
+* Locate any member of object name list appearing in command line
+*/
bool Parser::isWordPresent(char **wordArr) {
debugC(1, kDebugParser, "isWordPresent(%s)", wordArr[0]);
@@ -212,66 +228,47 @@ bool Parser::isWordPresent(char **wordArr) {
return false;
}
-// Locate word in list of nouns and return ptr to first string in noun list
+/**
+* Locate word in list of nouns and return ptr to first string in noun list
+*/
char *Parser::findNoun() {
debugC(1, kDebugParser, "findNoun()");
- for (int i = 0; _vm._arrayNouns[i]; i++) {
- for (int j = 0; strlen(_vm._arrayNouns[i][j]); j++) {
- if (strstr(_line, _vm._arrayNouns[i][j]))
- return _vm._arrayNouns[i][0];
+ for (int i = 0; _vm->_arrayNouns[i]; i++) {
+ for (int j = 0; strlen(_vm->_arrayNouns[i][j]); j++) {
+ if (strstr(_line, _vm->_arrayNouns[i][j]))
+ return _vm->_arrayNouns[i][0];
}
}
return 0;
}
-// Locate word in list of verbs and return ptr to first string in verb list
+/**
+* Locate word in list of verbs and return ptr to first string in verb list
+*/
char *Parser::findVerb() {
debugC(1, kDebugParser, "findVerb()");
- for (int i = 0; _vm._arrayVerbs[i]; i++) {
- for (int j = 0; strlen(_vm._arrayVerbs[i][j]); j++) {
- if (strstr(_line, _vm._arrayVerbs[i][j]))
- return _vm._arrayVerbs[i][0];
+ for (int i = 0; _vm->_arrayVerbs[i]; i++) {
+ for (int j = 0; strlen(_vm->_arrayVerbs[i][j]); j++) {
+ if (strstr(_line, _vm->_arrayVerbs[i][j]))
+ return _vm->_arrayVerbs[i][0];
}
}
return 0;
}
-// Describe any takeable objects visible in this screen
-void Parser::showTakeables() {
- debugC(1, kDebugParser, "showTakeables");
-
- for (int j = 0; j < _vm._numObj; j++) {
- object_t *obj = &_vm._objects[j];
- if ((obj->cycling != INVISIBLE) &&
- (obj->screenIndex == *_vm._screen_p) &&
- (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) {
- Utils::Box(BOX_ANY, "You can also see:\n%s.", _vm._arrayNouns[obj->nounIndex][LOOK_NAME]);
- }
- }
-}
-
-// Return TRUE if object being carried by hero
-bool Parser::isCarrying(uint16 wordIndex) {
- debugC(1, kDebugParser, "isCarrying(%d)", wordIndex);
-
- for (int i = 0; i < _vm._numObj; i++) {
- if ((wordIndex == _vm._objects[i].nounIndex) && _vm._objects[i].carriedFl)
- return true;
- }
- return false;
-}
-
-// Show user all objects being carried in a variable width 2 column format
+/**
+* Show user all objects being carried in a variable width 2 column format
+*/
void Parser::showDosInventory() {
debugC(1, kDebugParser, "showDosInventory()");
static const char *blanks = " ";
uint16 index = 0, len1 = 0, len2 = 0;
- for (int i = 0; i < _vm._numObj; i++) { // Find widths of 2 columns
- if (_vm._objects[i].carriedFl) {
- uint16 len = strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]);
+ for (int i = 0; i < _vm->_object->_numObj; i++) { // Find widths of 2 columns
+ if (_vm->_object->isCarried(i)) {
+ uint16 len = strlen(_vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][1]);
if (index++ & 1) // Right hand column
len2 = (len > len2) ? len : len2;
else
@@ -280,24 +277,24 @@ void Parser::showDosInventory() {
}
len1 += 1; // For gap between columns
- if (len1 + len2 < (uint16)strlen(_vm._textParser[kTBOutro]))
- len1 = strlen(_vm._textParser[kTBOutro]);
+ if (len1 + len2 < (uint16)strlen(_vm->_textParser[kTBOutro]))
+ len1 = strlen(_vm->_textParser[kTBOutro]);
char buffer[XBYTES *NUM_ROWS] = "\0";
- strncat(buffer, blanks, (len1 + len2 - strlen(_vm._textParser[kTBIntro])) / 2);
- strcat(strcat(buffer, _vm._textParser[kTBIntro]), "\n");
+ strncat(buffer, blanks, (len1 + len2 - strlen(_vm->_textParser[kTBIntro])) / 2);
+ strcat(strcat(buffer, _vm->_textParser[kTBIntro]), "\n");
index = 0;
- for (int i = 0; i < _vm._numObj; i++) { // Assign strings
- if (_vm._objects[i].carriedFl) {
+ for (int i = 0; i < _vm->_object->_numObj; i++) { // Assign strings
+ if (_vm->_object->isCarried(i)) {
if (index++ & 1)
- strcat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), "\n");
+ strcat(strcat(buffer, _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][1]), "\n");
else
- strncat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), blanks, len1 - strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]));
+ strncat(strcat(buffer, _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][1]), blanks, len1 - strlen(_vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][1]));
}
}
if (index & 1)
strcat(buffer, "\n");
- strcat(buffer, _vm._textParser[kTBOutro]);
+ strcat(buffer, _vm->_textParser[kTBOutro]);
Utils::Box(BOX_ANY, "%s", buffer);
}
diff --git a/engines/hugo/parser.h b/engines/hugo/parser.h
index 5226304d51..b98813c7bc 100644
--- a/engines/hugo/parser.h
+++ b/engines/hugo/parser.h
@@ -44,7 +44,7 @@ enum seqTextParser {
class Parser {
public:
- Parser(HugoEngine &vm);
+ Parser(HugoEngine *vm);
virtual ~Parser();
bool isWordPresent(char **wordArr);
@@ -55,16 +55,12 @@ public:
virtual void lineHandler() = 0;
protected:
- HugoEngine &_vm;
+ HugoEngine *_vm;
protected:
- bool isCarrying(uint16 wordIndex);
-
char *findNoun();
char *findVerb();
- void showTakeables();
-
private:
char _ringBuffer[32]; // Ring buffer
uint16 _putIndex;
@@ -76,7 +72,7 @@ private:
class Parser_v1w : public Parser {
public:
- Parser_v1w(HugoEngine &vm);
+ Parser_v1w(HugoEngine *vm);
~Parser_v1w();
virtual void lineHandler();
@@ -95,7 +91,7 @@ private:
class Parser_v1d : public Parser {
public:
- Parser_v1d(HugoEngine &vm);
+ Parser_v1d(HugoEngine *vm);
~Parser_v1d();
virtual void lineHandler();
@@ -113,15 +109,15 @@ protected:
class Parser_v2d : public Parser_v1d {
public:
- Parser_v2d(HugoEngine &vm);
+ Parser_v2d(HugoEngine *vm);
~Parser_v2d();
void lineHandler();
};
-
+
class Parser_v3d : public Parser_v1w {
public:
- Parser_v3d(HugoEngine &vm);
+ Parser_v3d(HugoEngine *vm);
~Parser_v3d();
void lineHandler();
diff --git a/engines/hugo/parser_v1d.cpp b/engines/hugo/parser_v1d.cpp
index 9364cd9532..9514f88178 100644
--- a/engines/hugo/parser_v1d.cpp
+++ b/engines/hugo/parser_v1d.cpp
@@ -39,72 +39,77 @@
#include "hugo/file.h"
#include "hugo/schedule.h"
#include "hugo/util.h"
+#include "hugo/object.h"
namespace Hugo {
-Parser_v1d::Parser_v1d(HugoEngine &vm) : Parser(vm) {
+Parser_v1d::Parser_v1d(HugoEngine *vm) : Parser(vm) {
}
Parser_v1d::~Parser_v1d() {
}
-// Locate word in list of nouns and return ptr to string in noun list
-// If n is NULL, start at beginning of list, else with n
+/**
+* Locate word in list of nouns and return ptr to string in noun list
+* If n is NULL, start at beginning of list, else with n
+*/
char *Parser_v1d::findNextNoun(char *noun) {
debugC(1, kDebugParser, "findNextNoun(%s)", noun);
int currNounIndex = -1;
if (noun) { // If noun not NULL, find index
- for (currNounIndex = 0; _vm._arrayNouns[currNounIndex]; currNounIndex++) {
- if (noun == _vm._arrayNouns[currNounIndex][0])
+ for (currNounIndex = 0; _vm->_arrayNouns[currNounIndex]; currNounIndex++) {
+ if (noun == _vm->_arrayNouns[currNounIndex][0])
break;
}
}
- for (int i = currNounIndex + 1; _vm._arrayNouns[i]; i++) {
- for (int j = 0; strlen(_vm._arrayNouns[i][j]); j++) {
- if (strstr(_line, _vm._arrayNouns[i][j]))
- return _vm._arrayNouns[i][0];
+ for (int i = currNounIndex + 1; _vm->_arrayNouns[i]; i++) {
+ for (int j = 0; strlen(_vm->_arrayNouns[i][j]); j++) {
+ if (strstr(_line, _vm->_arrayNouns[i][j]))
+ return _vm->_arrayNouns[i][0];
}
}
return 0;
}
-// Test whether hero is close to object. Return TRUE or FALSE
-// If no noun specified, check context flag in object before other tests.
-// If object not near, return suitable string; may be similar object closer
-// If radius is -1, treat radius as infinity
+/**
+* Test whether hero is close to object. Return TRUE or FALSE
+* If no noun specified, check context flag in object before other tests.
+* If object not near, return suitable string; may be similar object closer
+* If radius is -1, treat radius as infinity
+*/
bool Parser_v1d::isNear(char *verb, char *noun, object_t *obj, char *comment) {
debugC(1, kDebugParser, "isNear(%s, %s, obj, %s)", verb, noun, comment);
if (!noun && !obj->verbOnlyFl) { // No noun specified & object not context senesitive
return false;
- } else if (noun && (noun != _vm._arrayNouns[obj->nounIndex][0])) { // Noun specified & not same as object
+ } else if (noun && (noun != _vm->_arrayNouns[obj->nounIndex][0])) { // Noun specified & not same as object
return false;
} else if (obj->carriedFl) { // Object is being carried
return true;
- } else if (obj->screenIndex != *_vm._screen_p) { // Not in same screen
+ } else if (obj->screenIndex != *_vm->_screen_p) { // Not in same screen
if (obj->objValue)
- strcpy (comment, _vm._textParser[kCmtAny4]);
+ strcpy (comment, _vm->_textParser[kCmtAny4]);
return false;
}
if (obj->cycling == INVISIBLE) {
if (obj->seqNumb) { // There is an image
- strcpy(comment, _vm._textParser[kCmtAny5]);
+ strcpy(comment, _vm->_textParser[kCmtAny5]);
return false;
} else { // No image, assume visible
if ((obj->radius < 0) ||
- ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
- (abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ ((abs(obj->x - _vm->_hero->x) <= obj->radius) &&
+ (abs(obj->y - _vm->_hero->y - _vm->_hero->currImagePtr->y2) <= obj->radius))) {
return true;
} else {
// User is either not close enough (stationary, valueless objects)
// or is not carrying it (small, portable objects of value)
if (noun) { // Don't say unless object specified
- if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
- strcpy(comment, _vm._textParser[kCmtAny4]);
+ if (obj->objValue && (verb != _vm->_arrayVerbs[_vm->_take][0]))
+ strcpy(comment, _vm->_textParser[kCmtAny4]);
else
- strcpy(comment, _vm._textParser[kCmtClose]);
+ strcpy(comment, _vm->_textParser[kCmtClose]);
}
return false;
}
@@ -112,17 +117,17 @@ bool Parser_v1d::isNear(char *verb, char *noun, object_t *obj, char *comment) {
}
if ((obj->radius < 0) ||
- ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
- (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ ((abs(obj->x - _vm->_hero->x) <= obj->radius) &&
+ (abs(obj->y + obj->currImagePtr->y2 - _vm->_hero->y - _vm->_hero->currImagePtr->y2) <= obj->radius))) {
return true;
} else {
// User is either not close enough (stationary, valueless objects)
// or is not carrying it (small, portable objects of value)
if (noun) { // Don't say unless object specified
- if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
- strcpy(comment, _vm._textParser[kCmtAny4]);
+ if (obj->objValue && (verb != _vm->_arrayVerbs[_vm->_take][0]))
+ strcpy(comment, _vm->_textParser[kCmtAny4]);
else
- strcpy(comment, _vm._textParser[kCmtClose]);
+ strcpy(comment, _vm->_textParser[kCmtClose]);
}
return false;
}
@@ -130,9 +135,11 @@ bool Parser_v1d::isNear(char *verb, char *noun, object_t *obj, char *comment) {
return true;
}
-// Test whether supplied verb is one of the common variety for this object
-// say_ok needed for special case of take/drop which may be handled not only
-// here but also in a cmd_list with a donestr string simultaneously
+/**
+* Test whether supplied verb is one of the common variety for this object
+* say_ok needed for special case of take/drop which may be handled not only
+* here but also in a cmd_list with a donestr string simultaneously
+*/
bool Parser_v1d::isGenericVerb(char *word, object_t *obj) {
debugC(1, kDebugParser, "isGenericVerb(%s, object_t *obj)", word);
@@ -140,27 +147,27 @@ bool Parser_v1d::isGenericVerb(char *word, object_t *obj) {
return false;
// Following is equivalent to switch, but couldn't do one
- if (word == _vm._arrayVerbs[_vm._look][0]) {
+ if (word == _vm->_arrayVerbs[_vm->_look][0]) {
if ((LOOK & obj->genericCmd) == LOOK)
- Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[obj->dataIndex]);
else
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBUnusual_1d]);
- } else if (word == _vm._arrayVerbs[_vm._take][0]) {
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBUnusual_1d]);
+ } else if (word == _vm->_arrayVerbs[_vm->_take][0]) {
if (obj->carriedFl)
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBHave]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBHave]);
else if ((TAKE & obj->genericCmd) == TAKE)
takeObject(obj);
else if (!obj->verbOnlyFl) // Make sure not taking object in context!
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoUse]);
else
return false;
- } else if (word == _vm._arrayVerbs[_vm._drop][0]) {
+ } else if (word == _vm->_arrayVerbs[_vm->_drop][0]) {
if (!obj->carriedFl)
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBDontHave]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBDontHave]);
else if ((DROP & obj->genericCmd) == DROP)
dropObject(obj);
else
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNeed]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNeed]);
} else { // It was not a generic cmd
return false;
}
@@ -168,10 +175,12 @@ bool Parser_v1d::isGenericVerb(char *word, object_t *obj) {
return true;
}
-// Test whether supplied verb is included in the list of allowed verbs for
-// this object. If it is, then perform the tests on it from the cmd list
-// and if it passes, perform the actions in the action list. If the verb
-// is catered for, return TRUE
+/**
+* Test whether supplied verb is included in the list of allowed verbs for
+* this object. If it is, then perform the tests on it from the cmd list
+* and if it passes, perform the actions in the action list. If the verb
+* is catered for, return TRUE
+*/
bool Parser_v1d::isObjectVerb(char *word, object_t *obj) {
debugC(1, kDebugParser, "isObjectVerb(%s, object_t *obj)", word);
@@ -181,21 +190,21 @@ bool Parser_v1d::isObjectVerb(char *word, object_t *obj) {
return false;
int i;
- for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd
- if (!strcmp(word, _vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex][0])) // Is this verb catered for?
+ for (i = 0; _vm->_cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd
+ if (!strcmp(word, _vm->_arrayVerbs[_vm->_cmdList[cmdIndex][i].verbIndex][0])) // Is this verb catered for?
break;
}
- if (_vm._cmdList[cmdIndex][i].verbIndex == 0) // No
+ if (_vm->_cmdList[cmdIndex][i].verbIndex == 0) // No
return false;
// Verb match found, check all required objects are being carried
- cmd *cmnd = &_vm._cmdList[cmdIndex][i]; // ptr to struct cmd
+ cmd *cmnd = &_vm->_cmdList[cmdIndex][i]; // ptr to struct cmd
if (cmnd->reqIndex) { // At least 1 thing in list
- uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex]; // ptr to list of required objects
+ uint16 *reqs = _vm->_arrayReqs[cmnd->reqIndex]; // ptr to list of required objects
for (i = 0; reqs[i]; i++) { // for each obj
- if (!isCarrying(reqs[i])) {
- Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataNoCarryIndex]);
+ if (!_vm->_object->isCarrying(reqs[i])) {
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[cmnd->textDataNoCarryIndex]);
return true;
}
}
@@ -203,23 +212,25 @@ bool Parser_v1d::isObjectVerb(char *word, object_t *obj) {
// Required objects are present, now check state is correct
if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)){
- Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataWrongIndex]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[cmnd->textDataWrongIndex]);
return true;
}
// Everything checked. Change the state and carry out any actions
if (cmnd->reqState != DONT_CARE) // Don't change new state if required state didn't care
obj->state = cmnd->newState;
- Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataDoneIndex]);
- _vm.scheduler().insertActionList(cmnd->actIndex);
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[cmnd->textDataDoneIndex]);
+ _vm->_scheduler->insertActionList(cmnd->actIndex);
// Special case if verb is Take or Drop. Assume additional generic actions
- if ((word == _vm._arrayVerbs[_vm._take][0]) || (word == _vm._arrayVerbs[_vm._drop][0]))
+ if ((word == _vm->_arrayVerbs[_vm->_take][0]) || (word == _vm->_arrayVerbs[_vm->_drop][0]))
isGenericVerb(word, obj);
return true;
}
-// Print text for possible background object. Return TRUE if match found
-// Only match if both verb and noun found. Test_ca will match verb-only
+/**
+* Print text for possible background object. Return TRUE if match found
+* Only match if both verb and noun found. Test_ca will match verb-only
+*/
bool Parser_v1d::isBackgroundWord(char *noun, char *verb, objectList_t obj) {
debugC(1, kDebugParser, "isBackgroundWord(%s, %s, object_list_t obj)", noun, verb);
@@ -227,15 +238,17 @@ bool Parser_v1d::isBackgroundWord(char *noun, char *verb, objectList_t obj) {
return false;
for (int i = 0; obj[i].verbIndex; i++) {
- if ((verb == _vm._arrayVerbs[obj[i].verbIndex][0]) && (noun == _vm._arrayNouns[obj[i].nounIndex][0])) {
- Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
+ if ((verb == _vm->_arrayVerbs[obj[i].verbIndex][0]) && (noun == _vm->_arrayNouns[obj[i].nounIndex][0])) {
+ Utils::Box(BOX_ANY, "%s", _vm->_file->fetchString(obj[i].commentIndex));
return true;
}
}
return false;
}
-// Do all things necessary to carry an object
+/**
+* Do all things necessary to carry an object
+*/
void Parser_v1d::takeObject(object_t *obj) {
debugC(1, kDebugParser, "takeObject(object_t *obj)");
@@ -243,27 +256,31 @@ void Parser_v1d::takeObject(object_t *obj) {
if (obj->seqNumb) // Don't change if no image to display
obj->cycling = ALMOST_INVISIBLE;
- _vm.adjustScore(obj->objValue);
+ _vm->adjustScore(obj->objValue);
- Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[obj->nounIndex][TAKE_NAME]);
+ Utils::Box(BOX_ANY, TAKE_TEXT, _vm->_arrayNouns[obj->nounIndex][TAKE_NAME]);
}
-// Do all necessary things to drop an object
+/**
+* Do all necessary things to drop an object
+*/
void Parser_v1d::dropObject(object_t *obj) {
debugC(1, kDebugParser, "dropObject(object_t *obj)");
obj->carriedFl = false;
- obj->screenIndex = *_vm._screen_p;
+ obj->screenIndex = *_vm->_screen_p;
if (obj->seqNumb) // Don't change if no image to display
obj->cycling = NOT_CYCLING;
- obj->x = _vm._hero->x - 1;
- obj->y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1;
- _vm.adjustScore(-obj->objValue);
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBOk]);
+ obj->x = _vm->_hero->x - 1;
+ obj->y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->adjustScore(-obj->objValue);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBOk]);
}
-// Print text for possible background object. Return TRUE if match found
-// If test_noun TRUE, must have a noun given
+/**
+* Print text for possible background object. Return TRUE if match found
+* If test_noun TRUE, must have a noun given
+*/
bool Parser_v1d::isCatchallVerb(bool testNounFl, char *noun, char *verb, objectList_t obj) {
debugC(1, kDebugParser, "isCatchallVerb(%d, %s, %s, object_list_t obj)", (testNounFl) ? 1 : 0, noun, verb);
@@ -271,28 +288,30 @@ bool Parser_v1d::isCatchallVerb(bool testNounFl, char *noun, char *verb, objectL
return false;
for (int i = 0; obj[i].verbIndex; i++) {
- if ((verb == _vm._arrayVerbs[obj[i].verbIndex][0]) && ((noun == _vm._arrayNouns[obj[i].nounIndex][0]) || (obj[i].nounIndex == 0))) {
- Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
+ if ((verb == _vm->_arrayVerbs[obj[i].verbIndex][0]) && ((noun == _vm->_arrayNouns[obj[i].nounIndex][0]) || (obj[i].nounIndex == 0))) {
+ Utils::Box(BOX_ANY, "%s", _vm->_file->fetchString(obj[i].commentIndex));
return true;
}
}
return false;
}
-// Parse the user's line of text input. Generate events as necessary
+/**
+* Parse the user's line of text input. Generate events as necessary
+*/
void Parser_v1d::lineHandler() {
debugC(1, kDebugParser, "lineHandler()");
object_t *obj;
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
// Reset_prompt_line ();
Utils::strlwr(_line); // Convert to lower case
if (!strcmp("exit", _line) || strstr(_line, "quit")) {
- if (Utils::Box(BOX_YESNO, "%s", _vm._textParser[kTBExit_1d]) != 0)
- _vm.endGame();
+ if (Utils::Box(BOX_YESNO, "%s", _vm->_textParser[kTBExit_1d]) != 0)
+ _vm->endGame();
return;
}
@@ -301,14 +320,20 @@ void Parser_v1d::lineHandler() {
if (gameStatus.gameOverFl)
Utils::gameOverMsg();
else
-// _vm.file().saveOrRestore(true);
+// _vm->_file->saveOrRestore(true);
warning("STUB: saveOrRestore()");
+ // HACK: Currently use Win code
+ _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
return;
}
if (!strcmp("restore", _line)) {
-// _vm.file().saveOrRestore(false);
+// _vm->_file->saveOrRestore(false);
warning("STUB: saveOrRestore()");
+ // HACK: Currently use Win code
+ _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_scheduler->restoreScreen(*_vm->_screen_p);
+ gameStatus.viewState = V_PLAY;
return;
}
@@ -331,25 +356,25 @@ void Parser_v1d::lineHandler() {
do {
noun = findNextNoun(noun); // Find a noun in the line
// Must try at least once for objects allowing verb-context
- for (int i = 0; i < _vm._numObj; i++) {
- obj = &_vm._objects[i];
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ obj = &_vm->_object->_objects[i];
if (isNear(verb, noun, obj, farComment)) {
if (isObjectVerb(verb, obj) // Foreground object
|| isGenericVerb(verb, obj)) // Common action type
return;
}
}
- if ((*farComment == '\0') && isBackgroundWord(noun, verb, _vm._backgroundObjects[*_vm._screen_p]))
+ if ((*farComment == '\0') && isBackgroundWord(noun, verb, _vm->_backgroundObjects[*_vm->_screen_p]))
return;
} while (noun);
}
noun = findNextNoun(noun);
if (*farComment != '\0') // An object matched but not near enough
Utils::Box(BOX_ANY, "%s", farComment);
- else if (!isCatchallVerb(true, noun, verb, _vm._catchallList) &&
- !isCatchallVerb(false, noun, verb, _vm._backgroundObjects[*_vm._screen_p]) &&
- !isCatchallVerb(false, noun, verb, _vm._catchallList))
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh_1d]);
+ else if (!isCatchallVerb(true, noun, verb, _vm->_catchallList) &&
+ !isCatchallVerb(false, noun, verb, _vm->_backgroundObjects[*_vm->_screen_p]) &&
+ !isCatchallVerb(false, noun, verb, _vm->_catchallList))
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBEh_1d]);
}
} // End of namespace Hugo
diff --git a/engines/hugo/parser_v1w.cpp b/engines/hugo/parser_v1w.cpp
index 417b31e794..9155942130 100644
--- a/engines/hugo/parser_v1w.cpp
+++ b/engines/hugo/parser_v1w.cpp
@@ -40,17 +40,20 @@
#include "hugo/schedule.h"
#include "hugo/util.h"
#include "hugo/sound.h"
+#include "hugo/object.h"
namespace Hugo {
-Parser_v1w::Parser_v1w(HugoEngine &vm) : Parser(vm) {
+Parser_v1w::Parser_v1w(HugoEngine *vm) : Parser(vm) {
}
Parser_v1w::~Parser_v1w() {
}
-// Test whether command line contains a verb allowed by this object.
-// If it does, and the object is near and passes the tests in the command
-// list then carry out the actions in the action list and return TRUE
+/**
+* Test whether command line contains a verb allowed by this object.
+* If it does, and the object is near and passes the tests in the command
+* list then carry out the actions in the action list and return TRUE
+*/
bool Parser_v1w::isObjectVerb(object_t *obj, char *comment) {
debugC(1, kDebugParser, "isObjectVerb(object_t *obj, %s)", comment);
@@ -60,26 +63,26 @@ bool Parser_v1w::isObjectVerb(object_t *obj, char *comment) {
return false;
int i;
- for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd
- if (isWordPresent(_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex])) // Was this verb used?
+ for (i = 0; _vm->_cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd
+ if (isWordPresent(_vm->_arrayVerbs[_vm->_cmdList[cmdIndex][i].verbIndex])) // Was this verb used?
break;
}
- if (_vm._cmdList[cmdIndex][i].verbIndex == 0) // No verbs used.
+ if (_vm->_cmdList[cmdIndex][i].verbIndex == 0) // No verbs used.
return false;
// Verb match found. Check if object is Near
- char *verb = *_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex];
+ char *verb = *_vm->_arrayVerbs[_vm->_cmdList[cmdIndex][i].verbIndex];
if (!isNear(obj, verb, comment))
return false;
// Check all required objects are being carried
- cmd *cmnd = &_vm._cmdList[cmdIndex][i]; // ptr to struct cmd
+ cmd *cmnd = &_vm->_cmdList[cmdIndex][i]; // ptr to struct cmd
if (cmnd->reqIndex) { // At least 1 thing in list
- uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex]; // ptr to list of required objects
+ uint16 *reqs = _vm->_arrayReqs[cmnd->reqIndex]; // ptr to list of required objects
for (i = 0; reqs[i]; i++) { // for each obj
- if (!isCarrying(reqs[i])) {
- Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataNoCarryIndex]);
+ if (!_vm->_object->isCarrying(reqs[i])) {
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[cmnd->textDataNoCarryIndex]);
return true;
}
}
@@ -87,23 +90,25 @@ bool Parser_v1w::isObjectVerb(object_t *obj, char *comment) {
// Required objects are present, now check state is correct
if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)) {
- Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataWrongIndex]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[cmnd->textDataWrongIndex]);
return true;
}
// Everything checked. Change the state and carry out any actions
if (cmnd->reqState != DONT_CARE) // Don't change new state if required state didn't care
obj->state = cmnd->newState;
- Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataDoneIndex]);
- _vm.scheduler().insertActionList(cmnd->actIndex);
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[cmnd->textDataDoneIndex]);
+ _vm->_scheduler->insertActionList(cmnd->actIndex);
// See if any additional generic actions
- if ((verb == _vm._arrayVerbs[_vm._look][0]) || (verb == _vm._arrayVerbs[_vm._take][0]) || (verb == _vm._arrayVerbs[_vm._drop][0]))
+ if ((verb == _vm->_arrayVerbs[_vm->_look][0]) || (verb == _vm->_arrayVerbs[_vm->_take][0]) || (verb == _vm->_arrayVerbs[_vm->_drop][0]))
isGenericVerb(obj, comment);
return true;
}
-// Test whether command line contains one of the generic actions
+/**
+* Test whether command line contains one of the generic actions
+*/
bool Parser_v1w::isGenericVerb(object_t *obj, char *comment) {
debugC(1, kDebugParser, "isGenericVerb(object_t *obj, %s)", comment);
@@ -111,39 +116,39 @@ bool Parser_v1w::isGenericVerb(object_t *obj, char *comment) {
return false;
// Following is equivalent to switch, but couldn't do one
- if (isWordPresent(_vm._arrayVerbs[_vm._look]) && isNear(obj, _vm._arrayVerbs[_vm._look][0], comment)) {
+ if (isWordPresent(_vm->_arrayVerbs[_vm->_look]) && isNear(obj, _vm->_arrayVerbs[_vm->_look][0], comment)) {
// Test state-dependent look before general look
if ((obj->genericCmd & LOOK_S) == LOOK_S) {
- Utils::Box(BOX_ANY, "%s", _vm._textData[obj->stateDataIndex[obj->state]]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[obj->stateDataIndex[obj->state]]);
warning("isGenericVerb: use of state dependant look - To be validated");
} else {
if ((LOOK & obj->genericCmd) == LOOK) {
- if (_vm._textData[obj->dataIndex])
- Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]);
+ if (_vm->_textData[obj->dataIndex])
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[obj->dataIndex]);
else
return false;
} else {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBUnusual]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBUnusual]);
}
}
- } else if (isWordPresent(_vm._arrayVerbs[_vm._take]) && isNear(obj, _vm._arrayVerbs[_vm._take][0], comment)) {
+ } else if (isWordPresent(_vm->_arrayVerbs[_vm->_take]) && isNear(obj, _vm->_arrayVerbs[_vm->_take][0], comment)) {
if (obj->carriedFl)
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBHave]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBHave]);
else if ((TAKE & obj->genericCmd) == TAKE)
takeObject(obj);
else if (obj->cmdIndex != 0) // No comment if possible commands
return false;
else if (!obj->verbOnlyFl && (TAKE & obj->genericCmd) == TAKE) // Make sure not taking object in context!
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoUse]);
else
return false;
- } else if (isWordPresent(_vm._arrayVerbs[_vm._drop])) {
+ } else if (isWordPresent(_vm->_arrayVerbs[_vm->_drop])) {
if (!obj->carriedFl && ((DROP & obj->genericCmd) == DROP))
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBDontHave]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBDontHave]);
else if (obj->carriedFl && ((DROP & obj->genericCmd) == DROP))
dropObject(obj);
else if (obj->cmdIndex == 0)
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNeed]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNeed]);
else
return false;
} else { // It was not a generic cmd
@@ -153,63 +158,67 @@ bool Parser_v1w::isGenericVerb(object_t *obj, char *comment) {
return true;
}
-// Test whether hero is close to object. Return TRUE or FALSE
-// If object not near, return suitable comment; may be another object close
-// If radius is -1, treat radius as infinity
-// Verb is included to determine correct comment if not near
+/**
+* Test whether hero is close to object. Return TRUE or FALSE
+* If object not near, return suitable comment; may be another object close
+* If radius is -1, treat radius as infinity
+* Verb is included to determine correct comment if not near
+*/
bool Parser_v1w::isNear(object_t *obj, char *verb, char *comment) {
debugC(1, kDebugParser, "isNear(object_t *obj, %s, %s)", verb, comment);
if (obj->carriedFl) // Object is being carried
return true;
- if (obj->screenIndex != *_vm._screen_p) {
+ if (obj->screenIndex != *_vm->_screen_p) {
// Not in same screen
if (obj->objValue)
- strcpy(comment, _vm._textParser[kCmtAny1]);
+ strcpy(comment, _vm->_textParser[kCmtAny1]);
else
- strcpy(comment, _vm._textParser[kCmtAny2]);
+ strcpy(comment, _vm->_textParser[kCmtAny2]);
return false;
}
if (obj->cycling == INVISIBLE) {
if (obj->seqNumb) {
// There is an image
- strcpy(comment, _vm._textParser[kCmtAny3]);
+ strcpy(comment, _vm->_textParser[kCmtAny3]);
return false;
} else {
// No image, assume visible
if ((obj->radius < 0) ||
- ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
- (abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ ((abs(obj->x - _vm->_hero->x) <= obj->radius) &&
+ (abs(obj->y - _vm->_hero->y - _vm->_hero->currImagePtr->y2) <= obj->radius))) {
return true;
} else {
// User is not close enough
- if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
- strcpy(comment, _vm._textParser[kCmtAny1]);
+ if (obj->objValue && (verb != _vm->_arrayVerbs[_vm->_take][0]))
+ strcpy(comment, _vm->_textParser[kCmtAny1]);
else
- strcpy(comment, _vm._textParser[kCmtClose]);
+ strcpy(comment, _vm->_textParser[kCmtClose]);
return false;
}
}
}
if ((obj->radius < 0) ||
- ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
- (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ ((abs(obj->x - _vm->_hero->x) <= obj->radius) &&
+ (abs(obj->y + obj->currImagePtr->y2 - _vm->_hero->y - _vm->_hero->currImagePtr->y2) <= obj->radius))) {
return true;
} else {
// User is not close enough
- if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
- strcpy(comment, _vm._textParser[kCmtAny1]);
+ if (obj->objValue && (verb != _vm->_arrayVerbs[_vm->_take][0]))
+ strcpy(comment, _vm->_textParser[kCmtAny1]);
else
- strcpy(comment, _vm._textParser[kCmtClose]);
+ strcpy(comment, _vm->_textParser[kCmtClose]);
return false;
}
return true;
}
-// Do all things necessary to carry an object
+/**
+* Do all things necessary to carry an object
+*/
void Parser_v1w::takeObject(object_t *obj) {
debugC(1, kDebugParser, "takeObject(object_t *obj)");
@@ -217,48 +226,52 @@ void Parser_v1w::takeObject(object_t *obj) {
if (obj->seqNumb) { // Don't change if no image to display
obj->cycling = INVISIBLE;
}
- _vm.adjustScore(obj->objValue);
+ _vm->adjustScore(obj->objValue);
if (obj->seqNumb > 0) // If object has an image, force walk to dropped
obj->viewx = -1; // (possibly moved) object next time taken!
- Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[obj->nounIndex][TAKE_NAME]);
+ Utils::Box(BOX_ANY, TAKE_TEXT, _vm->_arrayNouns[obj->nounIndex][TAKE_NAME]);
}
-// Do all necessary things to drop an object
+/**
+* Do all necessary things to drop an object
+*/
void Parser_v1w::dropObject(object_t *obj) {
debugC(1, kDebugParser, "dropObject(object_t *obj)");
obj->carriedFl = false;
- obj->screenIndex = *_vm._screen_p;
+ obj->screenIndex = *_vm->_screen_p;
if ((obj->seqNumb > 1) || (obj->seqList[0].imageNbr > 1))
obj->cycling = CYCLE_FORWARD;
else
obj->cycling = NOT_CYCLING;
- obj->x = _vm._hero->x - 1;
- obj->y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1;
+ obj->x = _vm->_hero->x - 1;
+ obj->y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
obj->y = (obj->y + obj->currImagePtr->y2 < YPIX) ? obj->y : YPIX - obj->currImagePtr->y2 - 10;
- _vm.adjustScore(-obj->objValue);
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBOk]);
+ _vm->adjustScore(-obj->objValue);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBOk]);
}
-// Search for matching verbs in background command list.
-// Noun is not required. Return TRUE if match found
-// Note that if the background command list has match set TRUE then do not
-// print text if there are any recognizable nouns in the command line
+/**
+* Search for matching verbs in background command list.
+* Noun is not required. Return TRUE if match found
+* Note that if the background command list has match set TRUE then do not
+* print text if there are any recognizable nouns in the command line
+*/
bool Parser_v1w::isCatchallVerb(objectList_t obj) {
debugC(1, kDebugParser, "isCatchallVerb(object_list_t obj)");
for (int i = 0; obj[i].verbIndex != 0; i++) {
- if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 &&
+ if (isWordPresent(_vm->_arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 &&
(!obj[i].matchFl || !findNoun()) &&
((obj[i].roomState == DONT_CARE) ||
- (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) {
- Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
- _vm.scheduler().processBonus(obj[i].bonusIndex);
+ (obj[i].roomState == _vm->_screenStates[*_vm->_screen_p]))) {
+ Utils::Box(BOX_ANY, "%s", _vm->_file->fetchString(obj[i].commentIndex));
+ _vm->_scheduler->processBonus(obj[i].bonusIndex);
// If this is LOOK (without a noun), show any takeable objects
- if (*(_vm._arrayVerbs[obj[i].verbIndex]) == _vm._arrayVerbs[_vm._look][0])
- showTakeables();
+ if (*(_vm->_arrayVerbs[obj[i].verbIndex]) == _vm->_arrayVerbs[_vm->_look][0])
+ _vm->_object->showTakeables();
return true;
}
@@ -266,33 +279,37 @@ bool Parser_v1w::isCatchallVerb(objectList_t obj) {
return false;
}
-// Search for matching verb/noun pairs in background command list
-// Print text for possible background object. Return TRUE if match found
+/**
+* Search for matching verb/noun pairs in background command list
+* Print text for possible background object. Return TRUE if match found
+*/
bool Parser_v1w::isBackgroundWord(objectList_t obj) {
debugC(1, kDebugParser, "isBackgroundWord(object_list_t obj)");
for (int i = 0; obj[i].verbIndex != 0; i++) {
- if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) &&
- isWordPresent(_vm._arrayNouns[obj[i].nounIndex]) &&
+ if (isWordPresent(_vm->_arrayVerbs[obj[i].verbIndex]) &&
+ isWordPresent(_vm->_arrayNouns[obj[i].nounIndex]) &&
((obj[i].roomState == DONT_CARE) ||
- (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) {
- Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
- _vm.scheduler().processBonus(obj[i].bonusIndex);
+ (obj[i].roomState == _vm->_screenStates[*_vm->_screen_p]))) {
+ Utils::Box(BOX_ANY, "%s", _vm->_file->fetchString(obj[i].commentIndex));
+ _vm->_scheduler->processBonus(obj[i].bonusIndex);
return true;
}
}
return false;
}
-// Parse the user's line of text input. Generate events as necessary
+/**
+* Parse the user's line of text input. Generate events as necessary
+*/
void Parser_v1w::lineHandler() {
debugC(1, kDebugParser, "lineHandler()");
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
// Toggle God Mode
if (!strncmp(_line, "PPG", 3)) {
- _vm.sound().playSound(!_vm._soundTest, BOTH_CHANNELS, HIGH_PRI);
+ _vm->_sound->playSound(!_vm->_soundTest, BOTH_CHANNELS, HIGH_PRI);
gameStatus.godModeFl ^= 1;
return;
}
@@ -307,9 +324,9 @@ void Parser_v1w::lineHandler() {
if (gameStatus.godModeFl) {
// Special code to allow me to go straight to any screen
if (strstr(_line, "goto")) {
- for (int i = 0; i < _vm._numScreens; i++) {
- if (!strcmp(&_line[strlen("goto") + 1], _vm._screenNames[i])) {
- _vm.scheduler().newScreen(i);
+ for (int i = 0; i < _vm->_numScreens; i++) {
+ if (!strcmp(&_line[strlen("goto") + 1], _vm->_screenNames[i])) {
+ _vm->_scheduler->newScreen(i);
return;
}
}
@@ -317,17 +334,17 @@ void Parser_v1w::lineHandler() {
// Special code to allow me to get objects from anywhere
if (strstr(_line, "fetch all")) {
- for (int i = 0; i < _vm._numObj; i++) {
- if (_vm._objects[i].genericCmd & TAKE)
- takeObject(&_vm._objects[i]);
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ if (_vm->_object->_objects[i].genericCmd & TAKE)
+ takeObject(&_vm->_object->_objects[i]);
}
return;
}
if (strstr(_line, "fetch")) {
- for (int i = 0; i < _vm._numObj; i++) {
- if (!strcmp(&_line[strlen("fetch") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
- takeObject(&_vm._objects[i]);
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ if (!strcmp(&_line[strlen("fetch") + 1], _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][0])) {
+ takeObject(&_vm->_object->_objects[i]);
return;
}
}
@@ -335,9 +352,9 @@ void Parser_v1w::lineHandler() {
// Special code to allow me to goto objects
if (strstr(_line, "find")) {
- for (int i = 0; i < _vm._numObj; i++) {
- if (!strcmp(&_line[strlen("find") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
- _vm.scheduler().newScreen(_vm._objects[i].screenIndex);
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ if (!strcmp(&_line[strlen("find") + 1], _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][0])) {
+ _vm->_scheduler->newScreen(_vm->_object->_objects[i].screenIndex);
return;
}
}
@@ -347,19 +364,19 @@ void Parser_v1w::lineHandler() {
// Special meta commands
// EXIT/QUIT
if (!strcmp("exit", _line) || strstr(_line, "quit")) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBExit]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBExit]);
return;
}
// SAVE/RESTORE
if (!strcmp("save", _line) && gameStatus.viewState == V_PLAY) {
- _vm.file().saveGame(gameStatus.saveSlot, "Current game");
+ _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
return;
}
if (!strcmp("restore", _line) && (gameStatus.viewState == V_PLAY || gameStatus.viewState == V_IDLE)) {
- _vm.file().restoreGame(gameStatus.saveSlot);
- _vm.scheduler().restoreScreen(*_vm._screen_p);
+ _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_scheduler->restoreScreen(*_vm->_screen_p);
gameStatus.viewState = V_PLAY;
return;
}
@@ -379,9 +396,9 @@ void Parser_v1w::lineHandler() {
char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
// Test for nearby objects referenced explicitly
- for (int i = 0; i < _vm._numObj; i++) {
- object_t *obj = &_vm._objects[i];
- if (isWordPresent(_vm._arrayNouns[obj->nounIndex])) {
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ object_t *obj = &_vm->_object->_objects[i];
+ if (isWordPresent(_vm->_arrayNouns[obj->nounIndex])) {
if (isObjectVerb(obj, farComment) || isGenericVerb(obj, farComment))
return;
}
@@ -389,8 +406,8 @@ void Parser_v1w::lineHandler() {
// Test for nearby objects that only require a verb
// Note comment is unused if not near.
- for (int i = 0; i < _vm._numObj; i++) {
- object_t *obj = &_vm._objects[i];
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ object_t *obj = &_vm->_object->_objects[i];
if (obj->verbOnlyFl) {
char contextComment[XBYTES * 5] = ""; // Unused comment for context objects
if (isObjectVerb(obj, contextComment) || isGenericVerb(obj, contextComment))
@@ -399,13 +416,13 @@ void Parser_v1w::lineHandler() {
}
// No objects match command line, try background and catchall commands
- if (isBackgroundWord(_vm._backgroundObjects[*_vm._screen_p]))
+ if (isBackgroundWord(_vm->_backgroundObjects[*_vm->_screen_p]))
return;
- if (isCatchallVerb(_vm._backgroundObjects[*_vm._screen_p]))
+ if (isCatchallVerb(_vm->_backgroundObjects[*_vm->_screen_p]))
return;
- if (isBackgroundWord(_vm._catchallList))
+ if (isBackgroundWord(_vm->_catchallList))
return;
- if (isCatchallVerb(_vm._catchallList))
+ if (isCatchallVerb(_vm->_catchallList))
return;
// If a not-near comment was generated, print it
@@ -417,17 +434,17 @@ void Parser_v1w::lineHandler() {
// Nothing matches. Report recognition success to user.
char *verb = findVerb();
char *noun = findNoun();
- if (verb == _vm._arrayVerbs[_vm._look][0] && _maze.enabledFl) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBMaze]);
- showTakeables();
+ if (verb == _vm->_arrayVerbs[_vm->_look][0] && _maze.enabledFl) {
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBMaze]);
+ _vm->_object->showTakeables();
} else if (verb && noun) { // A combination I didn't think of
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoPoint]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoPoint]);
} else if (noun) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoun]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoun]);
} else if (verb) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBVerb]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBVerb]);
} else {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBEh]);
}
}
diff --git a/engines/hugo/parser_v2d.cpp b/engines/hugo/parser_v2d.cpp
index adaf5e9f9f..802f69056b 100644
--- a/engines/hugo/parser_v2d.cpp
+++ b/engines/hugo/parser_v2d.cpp
@@ -36,30 +36,35 @@
#include "hugo/hugo.h"
#include "hugo/parser.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
#include "hugo/util.h"
+#include "hugo/object.h"
namespace Hugo {
-Parser_v2d::Parser_v2d(HugoEngine &vm) : Parser_v1d(vm) {
+Parser_v2d::Parser_v2d(HugoEngine *vm) : Parser_v1d(vm) {
}
Parser_v2d::~Parser_v2d() {
}
-// Parse the user's line of text input. Generate events as necessary
+/**
+* Parse the user's line of text input. Generate events as necessary
+*/
void Parser_v2d::lineHandler() {
debugC(1, kDebugParser, "lineHandler()");
object_t *obj;
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
// Reset_prompt_line ();
Utils::strlwr(_line); // Convert to lower case
if (!strcmp("exit", _line) || strstr(_line, "quit")) {
- if (Utils::Box(BOX_YESNO, "%s", _vm._textParser[kTBExit_1d]) != 0)
- _vm.endGame();
+ if (Utils::Box(BOX_YESNO, "%s", _vm->_textParser[kTBExit_1d]) != 0)
+ _vm->endGame();
else
return;
}
@@ -70,15 +75,21 @@ void Parser_v2d::lineHandler() {
if (gameStatus.gameOverFl)
Utils::gameOverMsg();
else
-// _vm.file().saveOrRestore(true);
+// _vm->_file->saveOrRestore(true);
warning("STUB: saveOrRestore()");
+ // HACK: Currently use Win code
+ _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
return;
}
if (!strcmp("restore", _line)) {
_config.soundFl = false;
-// _vm.file().saveOrRestore(false);
+// _vm->_file->saveOrRestore(false);
warning("STUB: saveOrRestore()");
+ // HACK: Currently use Win code
+ _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_scheduler->restoreScreen(*_vm->_screen_p);
+ gameStatus.viewState = V_PLAY;
return;
}
@@ -101,35 +112,35 @@ void Parser_v2d::lineHandler() {
do {
noun = findNextNoun(noun); // Find a noun in the line
// Must try at least once for objects allowing verb-context
- for (int i = 0; i < _vm._numObj; i++) {
- obj = &_vm._objects[i];
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ obj = &_vm->_object->_objects[i];
if (isNear(verb, noun, obj, farComment)) {
if (isObjectVerb(verb, obj) // Foreground object
|| isGenericVerb(verb, obj)) // Common action type
return;
}
}
- if ((*farComment != '\0') && isBackgroundWord(noun, verb, _vm._backgroundObjects[*_vm._screen_p]))
+ if ((*farComment != '\0') && isBackgroundWord(noun, verb, _vm->_backgroundObjects[*_vm->_screen_p]))
return;
} while (noun);
}
noun = findNextNoun(noun);
- if ( !isCatchallVerb(true, noun, verb, _vm._backgroundObjects[*_vm._screen_p])
- && !isCatchallVerb(true, noun, verb, _vm._catchallList)
- && !isCatchallVerb(false, noun, verb, _vm._backgroundObjects[*_vm._screen_p])
- && !isCatchallVerb(false, noun, verb, _vm._catchallList)) {
+ if ( !isCatchallVerb(true, noun, verb, _vm->_backgroundObjects[*_vm->_screen_p])
+ && !isCatchallVerb(true, noun, verb, _vm->_catchallList)
+ && !isCatchallVerb(false, noun, verb, _vm->_backgroundObjects[*_vm->_screen_p])
+ && !isCatchallVerb(false, noun, verb, _vm->_catchallList)) {
if (*farComment != '\0') { // An object matched but not near enough
Utils::Box(BOX_ANY, "%s", farComment);
- } else if (_maze.enabledFl && (verb == _vm._arrayVerbs[_vm._look][0])) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBMaze]);
- showTakeables();
+ } else if (_maze.enabledFl && (verb == _vm->_arrayVerbs[_vm->_look][0])) {
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBMaze]);
+ _vm->_object->showTakeables();
} else if (verb && noun) { // A combination I didn't think of
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse_2d]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoUse_2d]);
} else if (verb || noun) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoun]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoun]);
} else {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh_2d]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBEh_2d]);
}
}
}
diff --git a/engines/hugo/parser_v3d.cpp b/engines/hugo/parser_v3d.cpp
index 501d8fba20..54c3e830ce 100644
--- a/engines/hugo/parser_v3d.cpp
+++ b/engines/hugo/parser_v3d.cpp
@@ -36,27 +36,31 @@
#include "hugo/hugo.h"
#include "hugo/parser.h"
+#include "hugo/file.h"
#include "hugo/schedule.h"
#include "hugo/util.h"
#include "hugo/sound.h"
+#include "hugo/object.h"
namespace Hugo {
-Parser_v3d::Parser_v3d(HugoEngine &vm) : Parser_v1w(vm) {
+Parser_v3d::Parser_v3d(HugoEngine *vm) : Parser_v1w(vm) {
}
Parser_v3d::~Parser_v3d() {
}
-// Parse the user's line of text input. Generate events as necessary
+/**
+* Parse the user's line of text input. Generate events as necessary
+*/
void Parser_v3d::lineHandler() {
debugC(1, kDebugParser, "lineHandler()");
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
// Toggle God Mode
if (!strncmp(_line, "PPG", 3)) {
- _vm.sound().playSound(!_vm._soundTest, BOTH_CHANNELS, HIGH_PRI);
+ _vm->_sound->playSound(!_vm->_soundTest, BOTH_CHANNELS, HIGH_PRI);
gameStatus.godModeFl ^= 1;
return;
}
@@ -71,9 +75,9 @@ void Parser_v3d::lineHandler() {
if (gameStatus.godModeFl) {
// Special code to allow me to go straight to any screen
if (strstr(_line, "goto")) {
- for (int i = 0; i < _vm._numScreens; i++) {
- if (!strcmp(&_line[strlen("goto") + 1], _vm._screenNames[i])) {
- _vm.scheduler().newScreen(i);
+ for (int i = 0; i < _vm->_numScreens; i++) {
+ if (!strcmp(&_line[strlen("goto") + 1], _vm->_screenNames[i])) {
+ _vm->_scheduler->newScreen(i);
return;
}
}
@@ -81,17 +85,17 @@ void Parser_v3d::lineHandler() {
// Special code to allow me to get objects from anywhere
if (strstr(_line, "fetch all")) {
- for (int i = 0; i < _vm._numObj; i++) {
- if (_vm._objects[i].genericCmd & TAKE)
- takeObject(&_vm._objects[i]);
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ if (_vm->_object->_objects[i].genericCmd & TAKE)
+ takeObject(&_vm->_object->_objects[i]);
}
return;
}
if (strstr(_line, "fetch")) {
- for (int i = 0; i < _vm._numObj; i++) {
- if (!strcmp(&_line[strlen("fetch") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
- takeObject(&_vm._objects[i]);
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ if (!strcmp(&_line[strlen("fetch") + 1], _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][0])) {
+ takeObject(&_vm->_object->_objects[i]);
return;
}
}
@@ -99,9 +103,9 @@ void Parser_v3d::lineHandler() {
// Special code to allow me to goto objects
if (strstr(_line, "find")) {
- for (int i = 0; i < _vm._numObj; i++) {
- if (!strcmp(&_line[strlen("find") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
- _vm.scheduler().newScreen(_vm._objects[i].screenIndex);
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ if (!strcmp(&_line[strlen("find") + 1], _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][0])) {
+ _vm->_scheduler->newScreen(_vm->_object->_objects[i].screenIndex);
return;
}
}
@@ -111,8 +115,8 @@ void Parser_v3d::lineHandler() {
// Special meta commands
// EXIT/QUIT
if (!strcmp("exit", _line) || strstr(_line, "quit")) {
- if (Utils::Box(BOX_YESNO, "%s", _vm._textParser[kTBExit_1d]) != 0)
- _vm.endGame();
+ if (Utils::Box(BOX_YESNO, "%s", _vm->_textParser[kTBExit_1d]) != 0)
+ _vm->endGame();
else
return;
}
@@ -123,15 +127,21 @@ void Parser_v3d::lineHandler() {
if (gameStatus.gameOverFl)
Utils::gameOverMsg();
else
-// _vm.file().saveOrRestore(true);
+// _vm->_file->saveOrRestore(true);
warning("STUB: saveOrRestore()");
+ // HACK: Currently use Win code
+ _vm->_file->saveGame(gameStatus.saveSlot, "Current game");
return;
}
if (!strcmp("restore", _line)) {
_config.soundFl = false;
-// _vm.file().saveOrRestore(false);
+// _vm->_file->saveOrRestore(false);
warning("STUB: saveOrRestore()");
+ // HACK: Currently use Win code
+ _vm->_file->restoreGame(gameStatus.saveSlot);
+ _vm->_scheduler->restoreScreen(*_vm->_screen_p);
+ gameStatus.viewState = V_PLAY;
return;
}
@@ -150,9 +160,9 @@ void Parser_v3d::lineHandler() {
char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
// Test for nearby objects referenced explicitly
- for (int i = 0; i < _vm._numObj; i++) {
- object_t *obj = &_vm._objects[i];
- if (isWordPresent(_vm._arrayNouns[obj->nounIndex])) {
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ object_t *obj = &_vm->_object->_objects[i];
+ if (isWordPresent(_vm->_arrayNouns[obj->nounIndex])) {
if (isObjectVerb(obj, farComment) || isGenericVerb(obj, farComment))
return;
}
@@ -160,8 +170,8 @@ void Parser_v3d::lineHandler() {
// Test for nearby objects that only require a verb
// Note comment is unused if not near.
- for (int i = 0; i < _vm._numObj; i++) {
- object_t *obj = &_vm._objects[i];
+ for (int i = 0; i < _vm->_object->_numObj; i++) {
+ object_t *obj = &_vm->_object->_objects[i];
if (obj->verbOnlyFl) {
char contextComment[XBYTES * 5] = ""; // Unused comment for context objects
if (isObjectVerb(obj, contextComment) || isGenericVerb(obj, contextComment))
@@ -170,13 +180,13 @@ void Parser_v3d::lineHandler() {
}
// No objects match command line, try background and catchall commands
- if (isBackgroundWord(_vm._backgroundObjects[*_vm._screen_p]))
+ if (isBackgroundWord(_vm->_backgroundObjects[*_vm->_screen_p]))
return;
- if (isCatchallVerb(_vm._backgroundObjects[*_vm._screen_p]))
+ if (isCatchallVerb(_vm->_backgroundObjects[*_vm->_screen_p]))
return;
- if (isBackgroundWord(_vm._catchallList))
+ if (isBackgroundWord(_vm->_catchallList))
return;
- if (isCatchallVerb(_vm._catchallList))
+ if (isCatchallVerb(_vm->_catchallList))
return;
// If a not-near comment was generated, print it
@@ -188,15 +198,15 @@ void Parser_v3d::lineHandler() {
// Nothing matches. Report recognition success to user.
char *verb = findVerb();
char *noun = findNoun();
-
+
if (verb && noun) { // A combination I didn't think of
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoPoint]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoPoint]);
} else if (noun) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoun]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBNoun]);
} else if (verb) {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBVerb]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBVerb]);
} else {
- Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh]);
+ Utils::Box(BOX_ANY, "%s", _vm->_textParser[kTBEh]);
}
}
diff --git a/engines/hugo/route.cpp b/engines/hugo/route.cpp
index 2ba95fb7d7..8ae5be10cd 100644
--- a/engines/hugo/route.cpp
+++ b/engines/hugo/route.cpp
@@ -38,16 +38,19 @@
#include "hugo/game.h"
#include "hugo/route.h"
#include "hugo/global.h"
+#include "hugo/object.h"
namespace Hugo {
-Route::Route(HugoEngine &vm) : _vm(vm) {
+Route::Route(HugoEngine *vm) : _vm(vm) {
}
-// Face hero in new direction, based on cursor key input by user.
+/**
+* Face hero in new direction, based on cursor key input by user.
+*/
void Route::setDirection(uint16 keyCode) {
debugC(1, kDebugRoute, "setDirection(%d)", keyCode);
- object_t *obj = _vm._hero; // Pointer to hero object
+ object_t *obj = _vm->_hero; // Pointer to hero object
// Set first image in sequence
switch (keyCode) {
@@ -78,15 +81,17 @@ void Route::setDirection(uint16 keyCode) {
}
}
-// Set hero walking, based on cursor key input by user.
-// Hitting same key twice will stop hero.
+/**
+* Set hero walking, based on cursor key input by user.
+* Hitting same key twice will stop hero.
+*/
void Route::setWalk(uint16 direction) {
debugC(1, kDebugRoute, "setWalk(%d)", direction);
static uint16 oldDirection = 0; // Last direction char
- object_t *obj = _vm._hero; // Pointer to hero object
+ object_t *obj = _vm->_hero; // Pointer to hero object
- if (_vm.getGameStatus().storyModeFl || obj->pathType != USER) // Make sure user has control
+ if (_vm->getGameStatus().storyModeFl || obj->pathType != USER) // Make sure user has control
return;
if (!obj->vx && !obj->vy)
@@ -141,20 +146,22 @@ void Route::setWalk(uint16 direction) {
}
}
-// Recursive algorithm! Searches from hero to dest_x, dest_y
-// Find horizontal line segment about supplied point and recursively
-// find line segments for each point above and below that segment.
-// When destination point found in segment, start surfacing and leave
-// a trail in segment[] from destination back to hero.
-//
-// Note: there is a bug which allows a route through a 1-pixel high
-// narrow gap if between 2 segments wide enough for hero. To work
-// around this, make sure any narrow gaps are 2 or more pixels high.
-// An example of this was the blocking guard in Hugo1/Dead-End.
+/**
+* Recursive algorithm! Searches from hero to dest_x, dest_y
+* Find horizontal line segment about supplied point and recursively
+* find line segments for each point above and below that segment.
+* When destination point found in segment, start surfacing and leave
+* a trail in segment[] from destination back to hero.
+*
+* Note: there is a bug which allows a route through a 1-pixel high
+* narrow gap if between 2 segments wide enough for hero. To work
+* around this, make sure any narrow gaps are 2 or more pixels high.
+* An example of this was the blocking guard in Hugo1/Dead-End.
+*/
void Route::segment(int16 x, int16 y) {
debugC(1, kDebugRoute, "segment(%d, %d)", x, y);
-// Note use of static - can't waste stack
+ // Note: use of static - can't waste stack
static image_pt p; // Ptr to _boundaryMap[y]
static segment_t *seg_p; // Ptr to segment
@@ -195,7 +202,7 @@ void Route::segment(int16 x, int16 y) {
if (y <= 0 || y >= YPIX - 1)
return;
- if (_vm._hero->x < x1) {
+ if (_vm->_hero->x < x1) {
// Hero x not in segment, search x1..x2
// Find all segments above current
for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
@@ -208,7 +215,7 @@ void Route::segment(int16 x, int16 y) {
if (_boundaryMap[y + 1][x] == 0)
segment(x, y + 1);
}
- } else if (_vm._hero->x + HERO_MAX_WIDTH > x2) {
+ } else if (_vm->_hero->x + HERO_MAX_WIDTH > x2) {
// Hero x not in segment, search x1..x2
// Find all segments above current
for (x = x2; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x >= x1; x--) {
@@ -224,22 +231,22 @@ void Route::segment(int16 x, int16 y) {
} else {
// Organize search around hero x position - this gives
// better chance for more direct route.
- for (x = _vm._hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
+ for (x = _vm->_hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
if (_boundaryMap[y - 1][x] == 0)
segment(x, y - 1);
}
- for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm._hero->x; x++) {
+ for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm->_hero->x; x++) {
if (_boundaryMap[y - 1][x] == 0)
segment(x, y - 1);
}
- for (x = _vm._hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
+ for (x = _vm->_hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
if (_boundaryMap[y + 1][x] == 0)
segment(x, y + 1);
}
- for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm._hero->x; x++) {
+ for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm->_hero->x; x++) {
if (_boundaryMap[y + 1][x] == 0)
segment(x, y + 1);
}
@@ -261,8 +268,10 @@ void Route::segment(int16 x, int16 y) {
}
}
-// Create and return ptr to new node. Initialize with previous node.
-// Returns 0 if MAX_NODES exceeded
+/**
+* Create and return ptr to new node. Initialize with previous node.
+* Returns 0 if MAX_NODES exceeded
+*/
Point *Route::newNode() {
debugC(1, kDebugRoute, "newNode");
@@ -273,10 +282,12 @@ Point *Route::newNode() {
return &_route[_routeListIndex];
}
-// Construct route to cx, cy. Return TRUE if successful.
-// 1. Copy boundary bitmap to local byte map (include object bases)
-// 2. Construct list of segments segment[] from hero to destination
-// 3. Compress to shortest route in route[]
+/**
+* Construct route to cx, cy. Return TRUE if successful.
+* 1. Copy boundary bitmap to local byte map (include object bases)
+* 2. Construct list of segments segment[] from hero to destination
+* 3. Compress to shortest route in route[]
+*/
bool Route::findRoute(int16 cx, int16 cy) {
debugC(1, kDebugRoute, "findRoute(%d, %d)", cx, cy);
@@ -289,32 +300,32 @@ bool Route::findRoute(int16 cx, int16 cy) {
_destY = cy; // Destination coords
_destX = cx; // Destination coords
- int16 herox1 = _vm._hero->x + _vm._hero->currImagePtr->x1; // Hero baseline
- int16 herox2 = _vm._hero->x + _vm._hero->currImagePtr->x2; // Hero baseline
- int16 heroy = _vm._hero->y + _vm._hero->currImagePtr->y2; // Hero baseline
+ int16 herox1 = _vm->_hero->x + _vm->_hero->currImagePtr->x1; // Hero baseline
+ int16 herox2 = _vm->_hero->x + _vm->_hero->currImagePtr->x2; // Hero baseline
+ int16 heroy = _vm->_hero->y + _vm->_hero->currImagePtr->y2; // Hero baseline
// Store all object baselines into objbound (except hero's = [0])
object_t *obj; // Ptr to object
int i;
- for (i = 1, obj = &_vm._objects[i]; i < _vm._numObj; i++, obj++) {
- if ((obj->screenIndex == *_vm._screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING))
- _vm.storeBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2);
+ for (i = 1, obj = &_vm->_object->_objects[i]; i < _vm->_object->_numObj; i++, obj++) {
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2);
}
// Combine objbound and boundary bitmaps to local byte map
for (int16 y = 0; y < YPIX; y++) {
for (int16 x = 0; x < XBYTES; x++) {
for (i = 0; i < 8; i++)
- _boundaryMap[y][x * 8 + i] = ((_vm.getObjectBoundaryOverlay()[y * XBYTES + x] | _vm.getBoundaryOverlay()[y * XBYTES + x]) & (0x80 >> i)) ? kMapBound : 0;
+ _boundaryMap[y][x * 8 + i] = ((_vm->getObjectBoundaryOverlay()[y * XBYTES + x] | _vm->getBoundaryOverlay()[y * XBYTES + x]) & (0x80 >> i)) ? kMapBound : 0;
}
}
-
+
// Clear all object baselines from objbound
- for (i = 0, obj = _vm._objects; i < _vm._numObj; i++, obj++) {
- if ((obj->screenIndex == *_vm._screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING))
- _vm.clearBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2);
+ for (i = 0, obj = _vm->_object->_objects; i < _vm->_object->_numObj; i++, obj++) {
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING))
+ _vm->clearBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2);
}
-
+
// Search from hero to destination
segment(herox1, heroy);
@@ -382,60 +393,61 @@ bool Route::findRoute(int16 cx, int16 cy) {
return true;
}
-// Process hero in route mode - called from Move_objects()
+/**
+* Process hero in route mode - called from Move_objects()
+*/
void Route::processRoute() {
debugC(1, kDebugRoute, "processRoute");
static bool turnedFl = false; // Used to get extra cylce for turning
// Current hero position
- int16 herox = _vm._hero->x + _vm._hero->currImagePtr->x1;
- int16 heroy = _vm._hero->y + _vm._hero->currImagePtr->y2;
- status_t &gameStatus = _vm.getGameStatus();
+ int16 herox = _vm->_hero->x + _vm->_hero->currImagePtr->x1;
+ int16 heroy = _vm->_hero->y + _vm->_hero->currImagePtr->y2;
+ status_t &gameStatus = _vm->getGameStatus();
Point *routeNode = &_route[gameStatus.routeIndex];
// Arrived at node?
if (abs(herox - routeNode->x) < DX + 1 && abs(heroy - routeNode->y) < DY) {
// DX too low
// Close enough - position hero exactly
- _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
- _vm._hero->y = _vm._hero->oldy = routeNode->y - _vm._hero->currImagePtr->y2;
- _vm._hero->vx = _vm._hero->vy = 0;
- _vm._hero->cycling = NOT_CYCLING;
+ _vm->_hero->x = _vm->_hero->oldx = routeNode->x - _vm->_hero->currImagePtr->x1;
+ _vm->_hero->y = _vm->_hero->oldy = routeNode->y - _vm->_hero->currImagePtr->y2;
+ _vm->_hero->vx = _vm->_hero->vy = 0;
+ _vm->_hero->cycling = NOT_CYCLING;
// Arrived at final node?
if (--gameStatus.routeIndex < 0) {
// See why we walked here
switch (gameStatus.go_for) {
case GO_EXIT: // Walked to an exit, proceed into it
- setWalk(_vm._hotspots[gameStatus.go_id].direction);
+ setWalk(_vm->_hotspots[gameStatus.go_id].direction);
break;
case GO_LOOK: // Look at an object
if (turnedFl) {
- _vm.lookObject(&_vm._objects[gameStatus.go_id]);
+ _vm->_object->lookObject(&_vm->_object->_objects[gameStatus.go_id]);
turnedFl = false;
} else {
- setDirection(_vm._objects[gameStatus.go_id].direction);
+ setDirection(_vm->_object->_objects[gameStatus.go_id].direction);
gameStatus.routeIndex++; // Come round again
turnedFl = true;
}
break;
case GO_GET: // Get (or use) an object
if (turnedFl) {
- _vm.useObject(gameStatus.go_id);
+ _vm->_object->useObject(gameStatus.go_id);
turnedFl = false;
} else {
- setDirection(_vm._objects[gameStatus.go_id].direction);
+ setDirection(_vm->_object->_objects[gameStatus.go_id].direction);
gameStatus.routeIndex++; // Come round again
turnedFl = true;
}
break;
- case GO_SPACE:
- warning("Unhandled gameStatus.go_for GO_STATUS");
+ default:
break;
}
}
- } else if (_vm._hero->vx == 0 && _vm._hero->vy == 0) {
+ } else if (_vm->_hero->vx == 0 && _vm->_hero->vy == 0) {
// Set direction of travel if at a node
// Note realignment when changing to (thinner) up/down sprite,
// otherwise hero could bump into boundaries along route.
@@ -445,25 +457,27 @@ void Route::processRoute() {
setWalk(Common::KEYCODE_LEFT);
} else if (heroy < routeNode->y) {
setWalk(Common::KEYCODE_DOWN);
- _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
+ _vm->_hero->x = _vm->_hero->oldx = routeNode->x - _vm->_hero->currImagePtr->x1;
} else if (heroy > routeNode->y) {
setWalk(Common::KEYCODE_UP);
- _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
+ _vm->_hero->x = _vm->_hero->oldx = routeNode->x - _vm->_hero->currImagePtr->x1;
}
}
}
-// Start a new route from hero to cx, cy
-// go_for is the purpose, id indexes the exit or object to walk to
-// Returns FALSE if route not found
+/**
+* Start a new route from hero to cx, cy
+* go_for is the purpose, id indexes the exit or object to walk to
+* Returns FALSE if route not found
+*/
bool Route::startRoute(go_t go_for, int16 id, int16 cx, int16 cy) {
debugC(1, kDebugRoute, "startRoute(%d, %d, %d, %d)", go_for, id, cx, cy);
// Don't attempt to walk if user does not have control
- if (_vm._hero->pathType != USER)
+ if (_vm->_hero->pathType != USER)
return false;
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
// if inventory showing, make it go away
if (gameStatus.inventoryState != I_OFF)
gameStatus.inventoryState = I_UP;
@@ -478,7 +492,7 @@ bool Route::startRoute(go_t go_for, int16 id, int16 cx, int16 cy) {
bool foundFl = false; // TRUE if route found ok
if ((foundFl = findRoute(cx, cy))) { // Found a route?
gameStatus.routeIndex = _routeListIndex; // Node index
- _vm._hero->vx = _vm._hero->vy = 0; // Stop manual motion
+ _vm->_hero->vx = _vm->_hero->vy = 0; // Stop manual motion
}
return foundFl;
diff --git a/engines/hugo/route.h b/engines/hugo/route.h
index 09b4575fcd..6aeb1a0b29 100644
--- a/engines/hugo/route.h
+++ b/engines/hugo/route.h
@@ -53,15 +53,15 @@ struct segment_t { // Search segment
class Route {
public:
- Route(HugoEngine &vm);
+ Route(HugoEngine *vm);
void processRoute();
- bool startRoute(go_t go_for, short id, short cx, short cy);
+ bool startRoute(go_t go_for, int16 id, int16 cx, int16 cy);
void setDirection(uint16 keyCode);
void setWalk(uint16 direction);
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
byte _boundaryMap[YPIX][XPIX]; // Boundary byte map
segment_t _segment[kMaxSeg]; // List of points in fill-path
diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp
index 22eb99c6dd..eca9a2050e 100644
--- a/engines/hugo/schedule.cpp
+++ b/engines/hugo/schedule.cpp
@@ -34,27 +34,23 @@
#include "common/system.h"
-#include "hugo/game.h"
#include "hugo/hugo.h"
#include "hugo/schedule.h"
-#include "hugo/global.h"
#include "hugo/file.h"
#include "hugo/display.h"
-#include "hugo/parser.h"
#include "hugo/util.h"
-#include "hugo/sound.h"
namespace Hugo {
-#define SIGN(X) ((X < 0) ? -1 : 1)
-
-Scheduler::Scheduler(HugoEngine &vm) : _vm(vm) {
+Scheduler::Scheduler(HugoEngine *vm) : _vm(vm), _actListArr(0) {
}
Scheduler::~Scheduler() {
}
-// Initialise the timer event queue
+/**
+* Initialise the timer event queue
+*/
void Scheduler::initEventQueue() {
debugC(1, kDebugSchedule, "initEventQueue");
@@ -72,111 +68,36 @@ void Scheduler::initEventQueue() {
_freeEvent = _events; // Free list is full
}
-// Return a ptr to an event structure from the free list
+/**
+* Return a ptr to an event structure from the free list
+*/
event_t *Scheduler::getQueue() {
debugC(4, kDebugSchedule, "getQueue");
if (!_freeEvent) // Error: no more events available
- Utils::Error(EVNT_ERR, "%s", "getQueue");
+ error("An error has occurred: %s", "getQueue");
event_t *resEvent = _freeEvent;
_freeEvent = _freeEvent->nextEvent;
resEvent->nextEvent = 0;
return resEvent;
}
-// Delete an event structure (i.e. return it to the free list)
-// Historical note: Originally event p was assumed to be at head of queue
-// (i.e. earliest) since all events were deleted in order when proceeding to
-// a new screen. To delete an event from the middle of the queue, the action
-// was overwritten to be ANULL. With the advent of GLOBAL events, Del_queue
-// was modified to allow deletes anywhere in the list, and the DEL_EVENT
-// action was modified to perform the actual delete.
-void Scheduler::delQueue(event_t *curEvent) {
- debugC(4, kDebugSchedule, "delQueue()");
-
- if (curEvent == _headEvent) { // If p was the head ptr
- _headEvent = curEvent->nextEvent; // then make new head_p
- } else { // Unlink p
- curEvent->prevEvent->nextEvent = curEvent->nextEvent;
- if (curEvent->nextEvent)
- curEvent->nextEvent->prevEvent = curEvent->prevEvent;
- else
- _tailEvent = curEvent->prevEvent;
- }
-
- if (_headEvent)
- _headEvent->prevEvent = 0; // Mark end of list
- else
- _tailEvent = 0; // Empty queue
-
- curEvent->nextEvent = _freeEvent; // Return p to free list
- if (_freeEvent) // Special case, if free list was empty
- _freeEvent->prevEvent = curEvent;
- _freeEvent = curEvent;
-}
-
-// Insert the action pointed to by p into the timer event queue
-// The queue goes from head (earliest) to tail (latest) timewise
-void Scheduler::insertAction(act *action) {
- debugC(1, kDebugSchedule, "insertAction() - Action type A%d", action->a0.actType);
-
- // First, get and initialise the event structure
- event_t *curEvent = getQueue();
- curEvent->action = action;
- switch (action->a0.actType) { // Assign whether local or global
- case AGSCHEDULE:
- curEvent->localActionFl = false; // Lasts over a new screen
- break;
- default:
- curEvent->localActionFl = true; // Rest are for current screen only
- break;
- }
-
- curEvent->time = action->a0.timer + getTicks(); // Convert rel to abs time
-
- // Now find the place to insert the event
- if (!_tailEvent) { // Empty queue
- _tailEvent = _headEvent = curEvent;
- curEvent->nextEvent = curEvent->prevEvent = 0;
- } else {
- event_t *wrkEvent = _tailEvent; // Search from latest time back
- bool found = false;
-
- while (wrkEvent && !found) {
- if (wrkEvent->time <= curEvent->time) { // Found if new event later
- found = true;
- if (wrkEvent == _tailEvent) // New latest in list
- _tailEvent = curEvent;
- else
- wrkEvent->nextEvent->prevEvent = curEvent;
- curEvent->nextEvent = wrkEvent->nextEvent;
- wrkEvent->nextEvent = curEvent;
- curEvent->prevEvent = wrkEvent;
- }
- wrkEvent = wrkEvent->prevEvent;
- }
-
- if (!found) { // Must be earliest in list
- _headEvent->prevEvent = curEvent; // So insert as new head
- curEvent->nextEvent = _headEvent;
- curEvent->prevEvent = 0;
- _headEvent = curEvent;
- }
- }
-}
-
+/**
+* Call Insert_action for each action in the list supplied
+*/
void Scheduler::insertActionList(uint16 actIndex) {
-// Call Insert_action for each action in the list supplied
debugC(1, kDebugSchedule, "insertActionList(%d)", actIndex);
- if (_vm._actListArr[actIndex]) {
- for (int i = 0; _vm._actListArr[actIndex][i].a0.actType != ANULL; i++)
- insertAction(&_vm._actListArr[actIndex][i]);
+ if (_actListArr[actIndex]) {
+ for (int i = 0; _actListArr[actIndex][i].a0.actType != ANULL; i++)
+ insertAction(&_actListArr[actIndex][i]);
}
}
+/**
+* Decode a string
+*/
void Scheduler::decodeString(char *line) {
-// Decode a string
debugC(1, kDebugSchedule, "decodeString(%s)", line);
static const char *cypher = getCypher();
@@ -186,360 +107,73 @@ void Scheduler::decodeString(char *line) {
debugC(1, kDebugSchedule, "result : %s", line);
}
-event_t *Scheduler::doAction(event_t *curEvent) {
-// This function performs the action in the event structure pointed to by p
-// It dequeues the event and returns it to the free list. It returns a ptr
-// to the next action in the list, except special case of NEW_SCREEN
- debugC(1, kDebugSchedule, "doAction - Event action type : %d", curEvent->action->a0.actType);
-
- status_t &gameStatus = _vm.getGameStatus();
- act *action = curEvent->action;
- char *response; // User's response string
- object_t *obj1;
- object_t *obj2;
- int dx, dy;
- event_t *wrkEvent; // Save ev_p->next_p for return
- event_t *saveEvent; // Used in DEL_EVENTS
-
- switch (action->a0.actType) {
- case ANULL: // Big NOP from DEL_EVENTS
- break;
- case ASCHEDULE: // act0: Schedule an action list
- insertActionList(action->a0.actIndex);
- break;
- case START_OBJ: // act1: Start an object cycling
- _vm._objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
- _vm._objects[action->a1.objNumb].cycling = action->a1.cycle;
- break;
- case INIT_OBJXY: // act2: Initialise an object
- _vm._objects[action->a2.objNumb].x = action->a2.x; // Coordinates
- _vm._objects[action->a2.objNumb].y = action->a2.y;
- break;
- case PROMPT: { // act3: Prompt user for key phrase
-// TODO : Add specific code for Hugo 1 DOS, which is handled differently,
- response = Utils::Box(BOX_PROMPT, "%s", _vm.file().fetchString(action->a3.promptIndex));
-
- warning("STUB: doAction(act3), expecting answer %s", response);
-
-// TODO : The answer of the player is not handled currently! Once it'll be read in the messageBox, uncomment this block
-#if 0
- bool found;
- char *tmpStr; // General purpose string ptr
-
- for (found = false, dx = 0; !found && (action->a3.responsePtr[dx] != -1); dx++) {
- tmpStr = _vm.file().Fetch_string(action->a3.responsePtr[dx]);
- if (strstr(Utils::strlwr(response) , tmpStr))
- found = true;
- }
-
- if (found)
- insertActionList(action->a3.actPassIndex);
- else
- insertActionList(action->a3.actFailIndex);
-#endif
-
- // HACK: As the answer is not read, currently it's always considered correct
- insertActionList(action->a3.actPassIndex);
- break;
- }
- case BKGD_COLOR: // act4: Set new background color
- _vm.screen().setBackgroundColor(action->a4.newBackgroundColor);
- break;
- case INIT_OBJVXY: // act5: Initialise an object
- _vm._objects[action->a5.objNumb].vx = action->a5.vx; // velocities
- _vm._objects[action->a5.objNumb].vy = action->a5.vy;
- break;
- case INIT_CARRY: // act6: Initialise an object
- _vm._objects[action->a6.objNumb].carriedFl = action->a6.carriedFl; // carried status
- break;
- case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
- _vm._objects[action->a7.objNumb].x = _vm._hero->x - 1;
- _vm._objects[action->a7.objNumb].y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1;
- _vm._objects[action->a7.objNumb].screenIndex = *_vm._screen_p; // Don't forget screen!
- break;
- case NEW_SCREEN: // act8: Start new screen
- newScreen(action->a8.screenIndex);
- break;
- case INIT_OBJSTATE: // act9: Initialise an object state
- _vm._objects[action->a9.objNumb].state = action->a9.newState;
- break;
- case INIT_PATH: // act10: Initialise an object path and velocity
- _vm._objects[action->a10.objNumb].pathType = (path_t) action->a10.newPathType;
- _vm._objects[action->a10.objNumb].vxPath = action->a10.vxPath;
- _vm._objects[action->a10.objNumb].vyPath = action->a10.vyPath;
- break;
- case COND_R: // act11: action lists conditional on object state
- if (_vm._objects[action->a11.objNumb].state == action->a11.stateReq)
- insertActionList(action->a11.actPassIndex);
- else
- insertActionList(action->a11.actFailIndex);
- break;
- case TEXT: // act12: Text box (CF WARN)
- Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(action->a12.stringIndex)); // Fetch string from file
- break;
- case SWAP_IMAGES: // act13: Swap 2 object images
- swapImages(action->a13.obj1, action->a13.obj2);
- break;
- case COND_SCR: // act14: Conditional on current screen
- if (_vm._objects[action->a14.objNumb].screenIndex == action->a14.screenReq)
- insertActionList(action->a14.actPassIndex);
- else
- insertActionList(action->a14.actFailIndex);
- break;
- case AUTOPILOT: // act15: Home in on a (stationary) object
- // object p1 will home in on object p2
- obj1 = &_vm._objects[action->a15.obj1];
- obj2 = &_vm._objects[action->a15.obj2];
- obj1->pathType = AUTO;
- dx = obj1->x + obj1->currImagePtr->x1 - obj2->x - obj2->currImagePtr->x1;
- dy = obj1->y + obj1->currImagePtr->y1 - obj2->y - obj2->currImagePtr->y1;
-
- if (dx == 0) // Don't EVER divide by zero!
- dx = 1;
- if (dy == 0)
- dy = 1;
-
- if (abs(dx) > abs(dy)) {
- obj1->vx = action->a15.dx * -SIGN(dx);
- obj1->vy = abs((action->a15.dy * dy) / dx) * -SIGN(dy);
- } else {
- obj1->vy = action->a15.dy * -SIGN(dy);
- obj1->vx = abs((action->a15.dx * dx) / dy) * -SIGN(dx);
- }
- break;
- case INIT_OBJ_SEQ: // act16: Set sequence number to use
- // Note: Don't set a sequence at time 0 of a new screen, it causes
- // problems clearing the boundary bits of the object! t>0 is safe
- _vm._objects[action->a16.objNumb].currImagePtr = _vm._objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
- break;
- case SET_STATE_BITS: // act17: OR mask with curr obj state
- _vm._objects[action->a17.objNumb].state |= action->a17.stateMask;
- break;
- case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
- _vm._objects[action->a18.objNumb].state &= ~action->a18.stateMask;
- break;
- case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
- if ((_vm._objects[action->a19.objNumb].state & action->a19.stateMask) == action->a19.stateMask)
- insertActionList(action->a19.actPassIndex);
- else
- insertActionList(action->a19.actFailIndex);
- break;
- case DEL_EVENTS: // act20: Remove all events of this action type
- // Note: actions are not deleted here, simply turned into NOPs!
- wrkEvent = _headEvent; // The earliest event
- while (wrkEvent) { // While events found in list
- saveEvent = wrkEvent->nextEvent;
- if (wrkEvent->action->a20.actType == action->a20.actTypeDel)
- delQueue(wrkEvent);
- wrkEvent = saveEvent;
- }
- break;
- case GAMEOVER: // act21: Game over!
- // NOTE: Must wait at least 1 tick before issuing this action if
- // any objects are to be made invisible!
- gameStatus.gameOverFl = true;
- break;
- case INIT_HH_COORD: // act22: Initialise an object to hero's actual coords
- _vm._objects[action->a22.objNumb].x = _vm._hero->x;
- _vm._objects[action->a22.objNumb].y = _vm._hero->y;
- _vm._objects[action->a22.objNumb].screenIndex = *_vm._screen_p;// Don't forget screen!
- break;
- case EXIT: // act23: Exit game back to DOS
- _vm.endGame();
- break;
- case BONUS: // act24: Get bonus score for action
- processBonus(action->a24.pointIndex);
- break;
- case COND_BOX: // act25: Conditional on bounding box
- obj1 = &_vm._objects[action->a25.objNumb];
- dx = obj1->x + obj1->currImagePtr->x1;
- dy = obj1->y + obj1->currImagePtr->y2;
- if ((dx >= action->a25.x1) && (dx <= action->a25.x2) &&
- (dy >= action->a25.y1) && (dy <= action->a25.y2))
- insertActionList(action->a25.actPassIndex);
- else
- insertActionList(action->a25.actFailIndex);
- break;
- case SOUND: // act26: Play a sound (or tune)
- if (action->a26.soundIndex < _vm._tunesNbr)
- _vm.sound().playMusic(action->a26.soundIndex);
- else
- _vm.sound().playSound(action->a26.soundIndex, BOTH_CHANNELS, MED_PRI);
- break;
- case ADD_SCORE: // act27: Add object's value to score
- _vm.adjustScore(_vm._objects[action->a27.objNumb].objValue);
- break;
- case SUB_SCORE: // act28: Subtract object's value from score
- _vm.adjustScore(-_vm._objects[action->a28.objNumb].objValue);
- break;
- case COND_CARRY: // act29: Conditional on object being carried
- if (_vm._objects[action->a29.objNumb].carriedFl)
- insertActionList(action->a29.actPassIndex);
- else
- insertActionList(action->a29.actFailIndex);
- break;
- case INIT_MAZE: // act30: Enable and init maze structure
- _maze.enabledFl = true;
- _maze.size = action->a30.mazeSize;
- _maze.x1 = action->a30.x1;
- _maze.y1 = action->a30.y1;
- _maze.x2 = action->a30.x2;
- _maze.y2 = action->a30.y2;
- _maze.x3 = action->a30.x3;
- _maze.x4 = action->a30.x4;
- _maze.firstScreenIndex = action->a30.firstScreenIndex;
- break;
- case EXIT_MAZE: // act31: Disable maze mode
- _maze.enabledFl = false;
- break;
- case INIT_PRIORITY:
- _vm._objects[action->a32.objNumb].priority = action->a32.priority;
- break;
- case INIT_SCREEN:
- _vm._objects[action->a33.objNumb].screenIndex = action->a33.screenIndex;
- break;
- case AGSCHEDULE: // act34: Schedule a (global) action list
- insertActionList(action->a34.actIndex);
- break;
- case REMAPPAL: // act35: Remap a palette color
- _vm.screen().remapPal(action->a35.oldColorIndex, action->a35.newColorIndex);
- break;
- case COND_NOUN: // act36: Conditional on noun mentioned
- if (_vm.parser().isWordPresent(_vm._arrayNouns[action->a36.nounIndex]))
- insertActionList(action->a36.actPassIndex);
- else
- insertActionList(action->a36.actFailIndex);
- break;
- case SCREEN_STATE: // act37: Set new screen state
- _vm._screenStates[action->a37.screenIndex] = action->a37.newState;
- break;
- case INIT_LIPS: // act38: Position lips on object
- _vm._objects[action->a38.lipsObjNumb].x = _vm._objects[action->a38.objNumb].x + action->a38.dxLips;
- _vm._objects[action->a38.lipsObjNumb].y = _vm._objects[action->a38.objNumb].y + action->a38.dyLips;
- _vm._objects[action->a38.lipsObjNumb].screenIndex = *_vm._screen_p; // Don't forget screen!
- _vm._objects[action->a38.lipsObjNumb].cycling = CYCLE_FORWARD;
- break;
- case INIT_STORY_MODE: // act39: Init story_mode flag
- // This is similar to the QUIET path mode, except that it is
- // independant of it and it additionally disables the ">" prompt
- gameStatus.storyModeFl = action->a39.storyModeFl;
-
- // End the game after story if this is special vendor demo mode
- if (gameStatus.demoFl && action->a39.storyModeFl == false)
- _vm.endGame();
- break;
- case WARN: // act40: Text box (CF TEXT)
- Utils::Box(BOX_OK, "%s", _vm.file().fetchString(action->a40.stringIndex));
- break;
- case COND_BONUS: // act41: Perform action if got bonus
- if (_vm._points[action->a41.BonusIndex].scoredFl)
- insertActionList(action->a41.actPassIndex);
- else
- insertActionList(action->a41.actFailIndex);
- break;
- case TEXT_TAKE: // act42: Text box with "take" message
- Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[_vm._objects[action->a42.objNumb].nounIndex][TAKE_NAME]);
- break;
- case YESNO: // act43: Prompt user for Yes or No
- warning("doAction(act43) - Yes/No Box");
- if (Utils::Box(BOX_YESNO, "%s", _vm.file().fetchString(action->a43.promptIndex)) != 0)
- insertActionList(action->a43.actYesIndex);
- else
- insertActionList(action->a43.actNoIndex);
- break;
- case STOP_ROUTE: // act44: Stop any route in progress
- gameStatus.routeIndex = -1;
- break;
- case COND_ROUTE: // act45: Conditional on route in progress
- if (gameStatus.routeIndex >= action->a45.routeIndex)
- insertActionList(action->a45.actPassIndex);
- else
- insertActionList(action->a45.actFailIndex);
- break;
- case INIT_JUMPEXIT: // act46: Init status.jumpexit flag
- // This is to allow left click on exit to get there immediately
- // For example the plane crash in Hugo2 where hero is invisible
- // Couldn't use INVISIBLE flag since conflicts with boat in Hugo1
- gameStatus.jumpExitFl = action->a46.jumpExitFl;
- break;
- case INIT_VIEW: // act47: Init object.viewx, viewy, dir
- _vm._objects[action->a47.objNumb].viewx = action->a47.viewx;
- _vm._objects[action->a47.objNumb].viewy = action->a47.viewy;
- _vm._objects[action->a47.objNumb].direction = action->a47.direction;
- break;
- case INIT_OBJ_FRAME: // act48: Set seq,frame number to use
- // Note: Don't set a sequence at time 0 of a new screen, it causes
- // problems clearing the boundary bits of the object! t>0 is safe
- _vm._objects[action->a48.objNumb].currImagePtr = _vm._objects[action->a48.objNumb].seqList[action->a48.seqIndex].seqPtr;
- for (dx = 0; dx < action->a48.frameIndex; dx++)
- _vm._objects[action->a48.objNumb].currImagePtr = _vm._objects[action->a48.objNumb].currImagePtr->nextSeqPtr;
- break;
- case OLD_SONG:
- //TODO For Hugo 1 and Hugo2 DOS: The songs were not stored in a DAT file, but directly as
- //strings. the current play_music should be modified to use a strings instead of reading
- //the file, in those cases. This replaces, for those DOS versions, act26.
- warning("STUB: doAction(act49)");
- break;
- default:
- Utils::Error(EVNT_ERR, "%s", "doAction");
- break;
- }
+/**
+* Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
+*/
+uint32 Scheduler::getWinTicks() {
+ debugC(3, kDebugSchedule, "getWinTicks");
- if (action->a0.actType == NEW_SCREEN) { // New_screen() deletes entire list
- return 0; // next_p = 0 since list now empty
- } else {
- wrkEvent = curEvent->nextEvent;
- delQueue(curEvent); // Return event to free list
- return wrkEvent; // Return next event ptr
- }
+ return _vm->getGameStatus().tick;
}
-// This is the scheduler which runs every tick. It examines the event queue
-// for any events whose time has come. It dequeues these events and performs
-// the action associated with the event, returning it to the free queue
-void Scheduler::runScheduler() {
- debugC(6, kDebugSchedule, "runScheduler");
+/**
+* Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
+* If update FALSE, simply return last known time
+* Note that this is real time unless a processing cycle takes longer than
+* a real tick, in which case the system tick is simply incremented
+*/
+uint32 Scheduler::getDosTicks(bool updateFl) {
+ debugC(5, kDebugSchedule, "getDosTicks(%s)", (updateFl) ? "TRUE" : "FALSE");
- status_t &gameStatus = _vm.getGameStatus();
- event_t *curEvent = _headEvent; // The earliest event
+ static uint32 tick = 0; // Current system time in ticks
+ static uint32 t_old = 0; // The previous wall time in ticks
- while (curEvent && curEvent->time <= gameStatus.tick) // While mature events found
- curEvent = doAction(curEvent); // Perform the action (returns next_p)
- gameStatus.tick++; // Accessed elsewhere via getTicks()
-}
+ uint32 t_now; // Current wall time in ticks
+
+ if (!updateFl)
+ return(tick);
-uint32 Scheduler::getTicks() {
-// Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
- debugC(3, kDebugSchedule, "getTicks");
+ if (t_old == 0)
+ t_old = (uint32) floor((double) (g_system->getMillis() * TPS / 1000));
+ /* Calculate current wall time in ticks */
+ t_now = g_system->getMillis() * TPS / 1000 ;
- return _vm.getGameStatus().tick;
+ if ((t_now - t_old) > 0) {
+ t_old = t_now;
+ tick++;
+ }
+ return(tick);
}
+/**
+* Add indecated bonus to score if not added already
+*/
void Scheduler::processBonus(int bonusIndex) {
-// Add indecated bonus to score if not added already
debugC(1, kDebugSchedule, "processBonus(%d)", bonusIndex);
- if (!_vm._points[bonusIndex].scoredFl) {
- _vm.adjustScore(_vm._points[bonusIndex].score);
- _vm._points[bonusIndex].scoredFl = true;
+ if (!_vm->_points[bonusIndex].scoredFl) {
+ _vm->adjustScore(_vm->_points[bonusIndex].score);
+ _vm->_points[bonusIndex].scoredFl = true;
}
}
-// Transition to a new screen as follows:
-// 1. Clear out all non-global events from event list.
-// 2. Set the new screen (in the hero object and any carried objects)
-// 3. Read in the screen files for the new screen
-// 4. Schedule action list for new screen
-// 5. Initialise prompt line and status line
+/**
+* Transition to a new screen as follows:
+* 1. Clear out all non-global events from event list.
+* 2. Set the new screen (in the hero object and any carried objects)
+* 3. Read in the screen files for the new screen
+* 4. Schedule action list for new screen
+* 5. Initialise prompt line and status line
+*/
void Scheduler::newScreen(int screenIndex) {
debugC(1, kDebugSchedule, "newScreen(%d)", screenIndex);
// Make sure the background file exists!
- if (!_vm.isPacked()) {
+ if (!_vm->isPacked()) {
char line[32];
- if (!_vm.file().fileExists(strcat(strncat(strcpy(line, _vm._picDir), _vm._screenNames[screenIndex], NAME_LEN), BKGEXT)) &&
- !_vm.file().fileExists(strcat(strcpy(line, _vm._screenNames[screenIndex]), ".ART"))) {
- Utils::Box(BOX_ANY, "%s", _vm._textSchedule[kSsNoBackground]);
+ if (!_vm->_file->fileExists(strcat(strncat(strcpy(line, _vm->_picDir), _vm->_screenNames[screenIndex], NAME_LEN), BKGEXT)) &&
+ !_vm->_file->fileExists(strcat(strcpy(line, _vm->_screenNames[screenIndex]), ".ART"))) {
+ error("Unable to find background file for %s", _vm->_screenNames[screenIndex]);
return;
}
}
@@ -555,29 +189,719 @@ void Scheduler::newScreen(int screenIndex) {
}
// 2. Set the new screen in the hero object and any being carried
- _vm.setNewScreen(screenIndex);
+ _vm->setNewScreen(screenIndex);
// 3. Read in new screen files
- _vm.readScreenFiles(screenIndex);
+ _vm->readScreenFiles(screenIndex);
// 4. Schedule action list for this screen
- _vm.screenActions(screenIndex);
+ _vm->screenActions(screenIndex);
// 5. Initialise prompt line and status line
- _vm.initNewScreenDisplay();
+ _vm->_screen->initNewScreenDisplay();
+}
+
+/**
+* Transition to a new screen as follows:
+* 1. Set the new screen (in the hero object and any carried objects)
+* 2. Read in the screen files for the new screen
+* 3. Initialise prompt line and status line
+*/
+void Scheduler::restoreScreen(int screenIndex) {
+ debugC(1, kDebugSchedule, "restoreScreen(%d)", screenIndex);
+
+ // 1. Set the new screen in the hero object and any being carried
+ _vm->setNewScreen(screenIndex);
+
+ // 2. Read in new screen files
+ _vm->readScreenFiles(screenIndex);
+
+ // 3. Initialise prompt line and status line
+ _vm->_screen->initNewScreenDisplay();
+}
+
+/**
+* Wait (if necessary) for next synchronizing tick
+* Slow machines won't make it by the end of tick, so will just plod on
+* at their own speed, not waiting here, but free running.
+* Note: DOS Versions only
+*/
+void Scheduler::waitForRefresh(void) {
+ debugC(1, kDebugSchedule, "waitForRefresh()");
+
+ static uint32 timeout = 0;
+ uint32 t;
+
+ if (timeout == 0)
+ timeout = getDosTicks(true);
+
+ while ((t = getDosTicks(true)) < timeout)
+ ;
+ timeout = ++t;
+}
+
+/**
+* Read kALnewscr used by maze (Hugo 2)
+*/
+void Scheduler::loadAlNewscrIndex(Common::File &in) {
+ debugC(6, kDebugSchedule, "loadAlNewscrIndex(&in)");
+
+ int numElem;
+ for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _vm->_gameVariant)
+ _alNewscrIndex = numElem;
+ }
+}
+
+/**
+* Load actListArr from Hugo.dat
+*/
+void Scheduler::loadActListArr(Common::File &in) {
+ debugC(6, kDebugSchedule, "loadActListArr(&in)");
+
+ int numElem, numSubElem, numSubAct;
+ for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _vm->_gameVariant) {
+ _actListArrSize = numElem;
+ _actListArr = (act **)malloc(sizeof(act *) * _actListArrSize);
+ for (int i = 0; i < _actListArrSize; i++) {
+ numSubElem = in.readUint16BE();
+ _actListArr[i] = (act *) malloc(sizeof(act) * (numSubElem + 1));
+ for (int j = 0; j < numSubElem; j++) {
+ _actListArr[i][j].a0.actType = (action_t) in.readByte();
+ switch (_actListArr[i][j].a0.actType) {
+ case ANULL: // -1
+ break;
+ case ASCHEDULE: // 0
+ _actListArr[i][j].a0.timer = in.readSint16BE();
+ _actListArr[i][j].a0.actIndex = in.readUint16BE();
+ break;
+ case START_OBJ: // 1
+ _actListArr[i][j].a1.timer = in.readSint16BE();
+ _actListArr[i][j].a1.objNumb = in.readSint16BE();
+ _actListArr[i][j].a1.cycleNumb = in.readSint16BE();
+ _actListArr[i][j].a1.cycle = (cycle_t) in.readByte();
+ break;
+ case INIT_OBJXY: // 2
+ _actListArr[i][j].a2.timer = in.readSint16BE();
+ _actListArr[i][j].a2.objNumb = in.readSint16BE();
+ _actListArr[i][j].a2.x = in.readSint16BE();
+ _actListArr[i][j].a2.y = in.readSint16BE();
+ break;
+ case PROMPT: // 3
+ _actListArr[i][j].a3.timer = in.readSint16BE();
+ _actListArr[i][j].a3.promptIndex = in.readSint16BE();
+ numSubAct = in.readUint16BE();
+ _actListArr[i][j].a3.responsePtr = (int *) malloc(sizeof(int) * numSubAct);
+ for (int k = 0; k < numSubAct; k++)
+ _actListArr[i][j].a3.responsePtr[k] = in.readSint16BE();
+ _actListArr[i][j].a3.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a3.actFailIndex = in.readUint16BE();
+ _actListArr[i][j].a3.encodedFl = (in.readByte() == 1) ? true : false;
+ break;
+ case BKGD_COLOR: // 4
+ _actListArr[i][j].a4.timer = in.readSint16BE();
+ _actListArr[i][j].a4.newBackgroundColor = in.readUint32BE();
+ break;
+ case INIT_OBJVXY: // 5
+ _actListArr[i][j].a5.timer = in.readSint16BE();
+ _actListArr[i][j].a5.objNumb = in.readSint16BE();
+ _actListArr[i][j].a5.vx = in.readSint16BE();
+ _actListArr[i][j].a5.vy = in.readSint16BE();
+ break;
+ case INIT_CARRY: // 6
+ _actListArr[i][j].a6.timer = in.readSint16BE();
+ _actListArr[i][j].a6.objNumb = in.readSint16BE();
+ _actListArr[i][j].a6.carriedFl = (in.readByte() == 1) ? true : false;
+ break;
+ case INIT_HF_COORD: // 7
+ _actListArr[i][j].a7.timer = in.readSint16BE();
+ _actListArr[i][j].a7.objNumb = in.readSint16BE();
+ break;
+ case NEW_SCREEN: // 8
+ _actListArr[i][j].a8.timer = in.readSint16BE();
+ _actListArr[i][j].a8.screenIndex = in.readSint16BE();
+ break;
+ case INIT_OBJSTATE: // 9
+ _actListArr[i][j].a9.timer = in.readSint16BE();
+ _actListArr[i][j].a9.objNumb = in.readSint16BE();
+ _actListArr[i][j].a9.newState = in.readByte();
+ break;
+ case INIT_PATH: // 10
+ _actListArr[i][j].a10.timer = in.readSint16BE();
+ _actListArr[i][j].a10.objNumb = in.readSint16BE();
+ _actListArr[i][j].a10.newPathType = in.readSint16BE();
+ _actListArr[i][j].a10.vxPath = in.readByte();
+ _actListArr[i][j].a10.vyPath = in.readByte();
+ break;
+ case COND_R: // 11
+ _actListArr[i][j].a11.timer = in.readSint16BE();
+ _actListArr[i][j].a11.objNumb = in.readSint16BE();
+ _actListArr[i][j].a11.stateReq = in.readByte();
+ _actListArr[i][j].a11.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a11.actFailIndex = in.readUint16BE();
+ break;
+ case TEXT: // 12
+ _actListArr[i][j].a12.timer = in.readSint16BE();
+ _actListArr[i][j].a12.stringIndex = in.readSint16BE();
+ break;
+ case SWAP_IMAGES: // 13
+ _actListArr[i][j].a13.timer = in.readSint16BE();
+ _actListArr[i][j].a13.obj1 = in.readSint16BE();
+ _actListArr[i][j].a13.obj2 = in.readSint16BE();
+ break;
+ case COND_SCR: // 14
+ _actListArr[i][j].a14.timer = in.readSint16BE();
+ _actListArr[i][j].a14.objNumb = in.readSint16BE();
+ _actListArr[i][j].a14.screenReq = in.readSint16BE();
+ _actListArr[i][j].a14.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a14.actFailIndex = in.readUint16BE();
+ break;
+ case AUTOPILOT: // 15
+ _actListArr[i][j].a15.timer = in.readSint16BE();
+ _actListArr[i][j].a15.obj1 = in.readSint16BE();
+ _actListArr[i][j].a15.obj2 = in.readSint16BE();
+ _actListArr[i][j].a15.dx = in.readByte();
+ _actListArr[i][j].a15.dy = in.readByte();
+ break;
+ case INIT_OBJ_SEQ: // 16
+ _actListArr[i][j].a16.timer = in.readSint16BE();
+ _actListArr[i][j].a16.objNumb = in.readSint16BE();
+ _actListArr[i][j].a16.seqIndex = in.readSint16BE();
+ break;
+ case SET_STATE_BITS: // 17
+ _actListArr[i][j].a17.timer = in.readSint16BE();
+ _actListArr[i][j].a17.objNumb = in.readSint16BE();
+ _actListArr[i][j].a17.stateMask = in.readSint16BE();
+ break;
+ case CLEAR_STATE_BITS: // 18
+ _actListArr[i][j].a18.timer = in.readSint16BE();
+ _actListArr[i][j].a18.objNumb = in.readSint16BE();
+ _actListArr[i][j].a18.stateMask = in.readSint16BE();
+ break;
+ case TEST_STATE_BITS: // 19
+ _actListArr[i][j].a19.timer = in.readSint16BE();
+ _actListArr[i][j].a19.objNumb = in.readSint16BE();
+ _actListArr[i][j].a19.stateMask = in.readSint16BE();
+ _actListArr[i][j].a19.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a19.actFailIndex = in.readUint16BE();
+ break;
+ case DEL_EVENTS: // 20
+ _actListArr[i][j].a20.timer = in.readSint16BE();
+ _actListArr[i][j].a20.actTypeDel = (action_t) in.readByte();
+ break;
+ case GAMEOVER: // 21
+ _actListArr[i][j].a21.timer = in.readSint16BE();
+ break;
+ case INIT_HH_COORD: // 22
+ _actListArr[i][j].a22.timer = in.readSint16BE();
+ _actListArr[i][j].a22.objNumb = in.readSint16BE();
+ break;
+ case EXIT: // 23
+ _actListArr[i][j].a23.timer = in.readSint16BE();
+ break;
+ case BONUS: // 24
+ _actListArr[i][j].a24.timer = in.readSint16BE();
+ _actListArr[i][j].a24.pointIndex = in.readSint16BE();
+ break;
+ case COND_BOX: // 25
+ _actListArr[i][j].a25.timer = in.readSint16BE();
+ _actListArr[i][j].a25.objNumb = in.readSint16BE();
+ _actListArr[i][j].a25.x1 = in.readSint16BE();
+ _actListArr[i][j].a25.y1 = in.readSint16BE();
+ _actListArr[i][j].a25.x2 = in.readSint16BE();
+ _actListArr[i][j].a25.y2 = in.readSint16BE();
+ _actListArr[i][j].a25.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a25.actFailIndex = in.readUint16BE();
+ break;
+ case SOUND: // 26
+ _actListArr[i][j].a26.timer = in.readSint16BE();
+ _actListArr[i][j].a26.soundIndex = in.readSint16BE();
+ break;
+ case ADD_SCORE: // 27
+ _actListArr[i][j].a27.timer = in.readSint16BE();
+ _actListArr[i][j].a27.objNumb = in.readSint16BE();
+ break;
+ case SUB_SCORE: // 28
+ _actListArr[i][j].a28.timer = in.readSint16BE();
+ _actListArr[i][j].a28.objNumb = in.readSint16BE();
+ break;
+ case COND_CARRY: // 29
+ _actListArr[i][j].a29.timer = in.readSint16BE();
+ _actListArr[i][j].a29.objNumb = in.readSint16BE();
+ _actListArr[i][j].a29.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a29.actFailIndex = in.readUint16BE();
+ break;
+ case INIT_MAZE: // 30
+ _actListArr[i][j].a30.timer = in.readSint16BE();
+ _actListArr[i][j].a30.mazeSize = in.readByte();
+ _actListArr[i][j].a30.x1 = in.readSint16BE();
+ _actListArr[i][j].a30.y1 = in.readSint16BE();
+ _actListArr[i][j].a30.x2 = in.readSint16BE();
+ _actListArr[i][j].a30.y2 = in.readSint16BE();
+ _actListArr[i][j].a30.x3 = in.readSint16BE();
+ _actListArr[i][j].a30.x4 = in.readSint16BE();
+ _actListArr[i][j].a30.firstScreenIndex = in.readByte();
+ break;
+ case EXIT_MAZE: // 31
+ _actListArr[i][j].a31.timer = in.readSint16BE();
+ break;
+ case INIT_PRIORITY: // 32
+ _actListArr[i][j].a32.timer = in.readSint16BE();
+ _actListArr[i][j].a32.objNumb = in.readSint16BE();
+ _actListArr[i][j].a32.priority = in.readByte();
+ break;
+ case INIT_SCREEN: // 33
+ _actListArr[i][j].a33.timer = in.readSint16BE();
+ _actListArr[i][j].a33.objNumb = in.readSint16BE();
+ _actListArr[i][j].a33.screenIndex = in.readSint16BE();
+ break;
+ case AGSCHEDULE: // 34
+ _actListArr[i][j].a34.timer = in.readSint16BE();
+ _actListArr[i][j].a34.actIndex = in.readUint16BE();
+ break;
+ case REMAPPAL: // 35
+ _actListArr[i][j].a35.timer = in.readSint16BE();
+ _actListArr[i][j].a35.oldColorIndex = in.readSint16BE();
+ _actListArr[i][j].a35.newColorIndex = in.readSint16BE();
+ break;
+ case COND_NOUN: // 36
+ _actListArr[i][j].a36.timer = in.readSint16BE();
+ _actListArr[i][j].a36.nounIndex = in.readUint16BE();
+ _actListArr[i][j].a36.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a36.actFailIndex = in.readUint16BE();
+ break;
+ case SCREEN_STATE: // 37
+ _actListArr[i][j].a37.timer = in.readSint16BE();
+ _actListArr[i][j].a37.screenIndex = in.readSint16BE();
+ _actListArr[i][j].a37.newState = in.readByte();
+ break;
+ case INIT_LIPS: // 38
+ _actListArr[i][j].a38.timer = in.readSint16BE();
+ _actListArr[i][j].a38.lipsObjNumb = in.readSint16BE();
+ _actListArr[i][j].a38.objNumb = in.readSint16BE();
+ _actListArr[i][j].a38.dxLips = in.readByte();
+ _actListArr[i][j].a38.dyLips = in.readByte();
+ break;
+ case INIT_STORY_MODE: // 39
+ _actListArr[i][j].a39.timer = in.readSint16BE();
+ _actListArr[i][j].a39.storyModeFl = (in.readByte() == 1);
+ break;
+ case WARN: // 40
+ _actListArr[i][j].a40.timer = in.readSint16BE();
+ _actListArr[i][j].a40.stringIndex = in.readSint16BE();
+ break;
+ case COND_BONUS: // 41
+ _actListArr[i][j].a41.timer = in.readSint16BE();
+ _actListArr[i][j].a41.BonusIndex = in.readSint16BE();
+ _actListArr[i][j].a41.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a41.actFailIndex = in.readUint16BE();
+ break;
+ case TEXT_TAKE: // 42
+ _actListArr[i][j].a42.timer = in.readSint16BE();
+ _actListArr[i][j].a42.objNumb = in.readSint16BE();
+ break;
+ case YESNO: // 43
+ _actListArr[i][j].a43.timer = in.readSint16BE();
+ _actListArr[i][j].a43.promptIndex = in.readSint16BE();
+ _actListArr[i][j].a43.actYesIndex = in.readUint16BE();
+ _actListArr[i][j].a43.actNoIndex = in.readUint16BE();
+ break;
+ case STOP_ROUTE: // 44
+ _actListArr[i][j].a44.timer = in.readSint16BE();
+ break;
+ case COND_ROUTE: // 45
+ _actListArr[i][j].a45.timer = in.readSint16BE();
+ _actListArr[i][j].a45.routeIndex = in.readSint16BE();
+ _actListArr[i][j].a45.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a45.actFailIndex = in.readUint16BE();
+ break;
+ case INIT_JUMPEXIT: // 46
+ _actListArr[i][j].a46.timer = in.readSint16BE();
+ _actListArr[i][j].a46.jumpExitFl = (in.readByte() == 1);
+ break;
+ case INIT_VIEW: // 47
+ _actListArr[i][j].a47.timer = in.readSint16BE();
+ _actListArr[i][j].a47.objNumb = in.readSint16BE();
+ _actListArr[i][j].a47.viewx = in.readSint16BE();
+ _actListArr[i][j].a47.viewy = in.readSint16BE();
+ _actListArr[i][j].a47.direction = in.readSint16BE();
+ break;
+ case INIT_OBJ_FRAME: // 48
+ _actListArr[i][j].a48.timer = in.readSint16BE();
+ _actListArr[i][j].a48.objNumb = in.readSint16BE();
+ _actListArr[i][j].a48.seqIndex = in.readSint16BE();
+ _actListArr[i][j].a48.frameIndex = in.readSint16BE();
+ break;
+ case OLD_SONG: //49
+ _actListArr[i][j].a49.timer = in.readSint16BE();
+ _actListArr[i][j].a49.soundIndex = in.readUint16BE();
+ break;
+ default:
+ error("Engine - Unknown action type encountered: %d", _actListArr[i][j].a0.actType);
+ }
+ }
+ _actListArr[i][numSubElem].a0.actType = ANULL;
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++) {
+ numSubAct = in.readByte();
+ switch (numSubAct) {
+ case ANULL: // -1
+ break;
+ case ASCHEDULE: // 0
+ in.readSint16BE();
+ in.readUint16BE();
+ break;
+ case START_OBJ: // 1
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_OBJXY: // 2
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case PROMPT: // 3
+ in.readSint16BE();
+ in.readSint16BE();
+ numSubAct = in.readUint16BE();
+ for (int k = 0; k < numSubAct; k++)
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readByte();
+ break;
+ case BKGD_COLOR: // 4
+ in.readSint16BE();
+ in.readUint32BE();
+ break;
+ case INIT_OBJVXY: // 5
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case INIT_CARRY: // 6
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_HF_COORD: // 7
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case NEW_SCREEN: // 8
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case INIT_OBJSTATE: // 9
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_PATH: // 10
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ break;
+ case COND_R: // 11
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case TEXT: // 12
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case SWAP_IMAGES: // 13
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_SCR: // 14
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case AUTOPILOT: // 15
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ break;
+ case INIT_OBJ_SEQ: // 16
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case SET_STATE_BITS: // 17
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case CLEAR_STATE_BITS: // 18
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case TEST_STATE_BITS: // 19
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case DEL_EVENTS: // 20
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case GAMEOVER: // 21
+ in.readSint16BE();
+ break;
+ case INIT_HH_COORD: // 22
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case EXIT: // 23
+ in.readSint16BE();
+ break;
+ case BONUS: // 24
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_BOX: // 25
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case SOUND: // 26
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case ADD_SCORE: // 27
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case SUB_SCORE: // 28
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_CARRY: // 29
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case INIT_MAZE: // 30
+ in.readSint16BE();
+ in.readByte();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case EXIT_MAZE: // 31
+ in.readSint16BE();
+ break;
+ case INIT_PRIORITY: // 32
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_SCREEN: // 33
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case AGSCHEDULE: // 34
+ in.readSint16BE();
+ in.readUint16BE();
+ break;
+ case REMAPPAL: // 35
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_NOUN: // 36
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case SCREEN_STATE: // 37
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_LIPS: // 38
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ break;
+ case INIT_STORY_MODE: // 39
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case WARN: // 40
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_BONUS: // 41
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case TEXT_TAKE: // 42
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case YESNO: // 43
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case STOP_ROUTE: // 44
+ in.readSint16BE();
+ break;
+ case COND_ROUTE: // 45
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case INIT_JUMPEXIT: // 46
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_VIEW: // 47
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case INIT_OBJ_FRAME: // 48
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case OLD_SONG: //49
+ in.readSint16BE();
+ in.readUint16BE();
+ break;
+ default:
+ error("Engine - Unknown action type encountered %d - variante %d pos %d.%d", numSubAct, varnt, i, j);
+ }
+ }
+ }
+ }
+ }
}
-// Write the event queue to the file with handle f
-// Note that we convert all the event structure ptrs to indexes
-// using -1 for NULL. We can't convert the action ptrs to indexes
-// so we save address of first dummy action ptr to compare on restore.
+void Scheduler::freeActListArr() {
+ debugC(6, kDebugSchedule, "freeActListArr()");
+
+ if (_actListArr) {
+ for (int i = 0; i < _actListArrSize; i++) {
+ for (int j = 0; _actListArr[i][j].a0.actType != ANULL; j++) {
+ if (_actListArr[i][j].a0.actType == PROMPT)
+ free(_actListArr[i][j].a3.responsePtr);
+ }
+ free(_actListArr[i]);
+ }
+ free(_actListArr);
+ }
+}
+
+/**
+* Maze mode is enabled. Check to see whether hero has crossed the maze
+* bounding box, if so, go to the next room
+*/
+void Scheduler::processMaze(int x1, int x2, int y1, int y2) {
+ debugC(1, kDebugSchedule, "processMaze");
+
+ status_t &gameStatus = _vm->getGameStatus();
+
+ if (x1 < _maze.x1) {
+ // Exit west
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_vm->_screen_p - 1;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x2 - SHIFT - (x2 - x1);
+ _actListArr[_alNewscrIndex][0].a2.y = _vm->_hero->y;
+ gameStatus.routeIndex = -1;
+ insertActionList(_alNewscrIndex);
+ } else if (x2 > _maze.x2) {
+ // Exit east
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_vm->_screen_p + 1;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x1 + SHIFT;
+ _actListArr[_alNewscrIndex][0].a2.y = _vm->_hero->y;
+ gameStatus.routeIndex = -1;
+ insertActionList(_alNewscrIndex);
+ } else if (y1 < _maze.y1 - SHIFT) {
+ // Exit north
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_vm->_screen_p - _maze.size;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x3;
+ _actListArr[_alNewscrIndex][0].a2.y = _maze.y2 - SHIFT - (y2 - y1);
+ gameStatus.routeIndex = -1;
+ insertActionList(_alNewscrIndex);
+ } else if (y2 > _maze.y2 - SHIFT / 2) {
+ // Exit south
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_vm->_screen_p + _maze.size;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x4;
+ _actListArr[_alNewscrIndex][0].a2.y = _maze.y1 + SHIFT;
+ gameStatus.routeIndex = -1;
+ insertActionList(_alNewscrIndex);
+ }
+}
+
+/**
+* Write the event queue to the file with handle f
+* Note that we convert all the event structure ptrs to indexes
+* using -1 for NULL. We can't convert the action ptrs to indexes
+* so we save address of first dummy action ptr to compare on restore.
+*/
void Scheduler::saveEvents(Common::WriteStream *f) {
debugC(1, kDebugSchedule, "saveEvents()");
- uint32 curTime = getTicks();
- event_t saveEventArr[kMaxEvents]; // Convert event ptrs to indexes
+ f->writeUint32BE(getTicks());
+
+ int16 freeIndex = (_freeEvent == 0) ? -1 : _freeEvent - _events;
+ int16 headIndex = (_headEvent == 0) ? -1 : _headEvent - _events;
+ int16 tailIndex = (_tailEvent == 0) ? -1 : _tailEvent - _events;
+
+ f->writeSint16BE(freeIndex);
+ f->writeSint16BE(headIndex);
+ f->writeSint16BE(tailIndex);
// Convert event ptrs to indexes
+ event_t saveEventArr[kMaxEvents]; // Convert event ptrs to indexes
for (int16 i = 0; i < kMaxEvents; i++) {
event_t *wrkEvent = &_events[i];
saveEventArr[i] = *wrkEvent;
@@ -585,31 +909,22 @@ void Scheduler::saveEvents(Common::WriteStream *f) {
saveEventArr[i].nextEvent = (wrkEvent->nextEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events);
}
- int16 freeIndex = (_freeEvent == 0) ? -1 : _freeEvent - _events;
- int16 headIndex = (_headEvent == 0) ? -1 : _headEvent - _events;
- int16 tailIndex = (_tailEvent == 0) ? -1 : _tailEvent - _events;
-
- f->write(&curTime, sizeof(curTime));
- f->write(&freeIndex, sizeof(freeIndex));
- f->write(&headIndex, sizeof(headIndex));
- f->write(&tailIndex, sizeof(tailIndex));
f->write(saveEventArr, sizeof(saveEventArr));
+ warning("TODO: serialize saveEventArr");
}
-// Restore the event list from file with handle f
+/**
+* Restore the event list from file with handle f
+*/
void Scheduler::restoreEvents(Common::SeekableReadStream *f) {
debugC(1, kDebugSchedule, "restoreEvents");
- uint32 saveTime;
- int16 freeIndex; // Free list index
- int16 headIndex; // Head of list index
- int16 tailIndex; // Tail of list index
event_t savedEvents[kMaxEvents]; // Convert event ptrs to indexes
- f->read(&saveTime, sizeof(saveTime)); // time of save
- f->read(&freeIndex, sizeof(freeIndex));
- f->read(&headIndex, sizeof(headIndex));
- f->read(&tailIndex, sizeof(tailIndex));
+ uint32 saveTime = f->readUint32BE(); // time of save
+ int16 freeIndex = f->readSint16BE(); // Free list index
+ int16 headIndex = f->readSint16BE(); // Head of list index
+ int16 tailIndex = f->readSint16BE(); // Tail of list index
f->read(savedEvents, sizeof(savedEvents));
event_t *wrkEvent;
@@ -633,44 +948,4 @@ void Scheduler::restoreEvents(Common::SeekableReadStream *f) {
}
}
-void Scheduler::restoreScreen(int screenIndex) {
-// Transition to a new screen as follows:
-// 1. Set the new screen (in the hero object and any carried objects)
-// 2. Read in the screen files for the new screen
-// 3. Initialise prompt line and status line
-
- debugC(1, kDebugSchedule, "restoreScreen(%d)", screenIndex);
-
- // 1. Set the new screen in the hero object and any being carried
- _vm.setNewScreen(screenIndex);
-
- // 2. Read in new screen files
- _vm.readScreenFiles(screenIndex);
-
- // 3. Initialise prompt line and status line
- _vm.initNewScreenDisplay();
-}
-
-void Scheduler::swapImages(int objNumb1, int objNumb2) {
-// Swap all the images of one object with another. Set hero_image (we make
-// the assumption for now that the first obj is always the HERO) to the object
-// number of the swapped image
- debugC(1, kDebugSchedule, "swapImages(%d, %d)", objNumb1, objNumb2);
-
- _vm.file().saveSeq(&_vm._objects[objNumb1]);
-
- seqList_t tmpSeqList[MAX_SEQUENCES];
- int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
-
- memcpy(tmpSeqList, _vm._objects[objNumb1].seqList, seqListSize);
- memcpy(_vm._objects[objNumb1].seqList, _vm._objects[objNumb2].seqList, seqListSize);
- memcpy(_vm._objects[objNumb2].seqList, tmpSeqList, seqListSize);
- _vm.file().restoreSeq(&_vm._objects[objNumb1]);
- _vm._objects[objNumb2].currImagePtr = _vm._objects[objNumb2].seqList[0].seqPtr;
- _vm._heroImage = (_vm._heroImage == HERO) ? objNumb2 : HERO;
-
- // Make sure baseline stays constant
- _vm._objects[objNumb1].y += _vm._objects[objNumb2].currImagePtr->y2 - _vm._objects[objNumb1].currImagePtr->y2;
-}
-
} // End of namespace Hugo
diff --git a/engines/hugo/schedule.h b/engines/hugo/schedule.h
index b77abd4302..32e988310e 100644
--- a/engines/hugo/schedule.h
+++ b/engines/hugo/schedule.h
@@ -33,8 +33,11 @@
#ifndef HUGO_SCHEDULE_H
#define HUGO_SCHEDULE_H
+#include "common/file.h"
+
namespace Hugo {
+#define SIGN(X) ((X < 0) ? -1 : 1)
#define kMaxEvents 50 // Max events in event queue
struct event_t {
@@ -47,59 +50,96 @@ struct event_t {
class Scheduler {
public:
- Scheduler(HugoEngine &vm);
+ Scheduler(HugoEngine *vm);
virtual ~Scheduler();
- void initEventQueue();
- void insertAction(act *action);
- void insertActionList(uint16 actIndex);
- void decodeString(char *line);
- void runScheduler();
- uint32 getTicks();
- void processBonus(int bonusIndex);
- void newScreen(int screenIndex);
- void restoreEvents(Common::SeekableReadStream *f);
- void saveEvents(Common::WriteStream *f);
- void restoreScreen(int screenIndex);
- void swapImages(int objNumb1, int objNumb2);
+ virtual uint32 getTicks() = 0;
+
+ virtual void runScheduler() = 0;
-private:
- enum seqTextSchedule {
- kSsNoBackground = 0,
- kSsBadSaveGame = 1
- };
+ void decodeString(char *line);
+ void freeActListArr();
+ void initEventQueue();
+ void insertActionList(uint16 actIndex);
+ void loadActListArr(Common::File &in);
+ void loadAlNewscrIndex(Common::File &in);
+ void newScreen(int screenIndex);
+ void processBonus(int bonusIndex);
+ void processMaze(int x1, int x2, int y1, int y2);
+ void restoreScreen(int screenIndex);
+ void restoreEvents(Common::SeekableReadStream *f);
+ void saveEvents(Common::WriteStream *f);
+ void waitForRefresh(void);
- HugoEngine &_vm;
+protected:
+ HugoEngine *_vm;
- event_t _events[kMaxEvents]; // Statically declare event structures
+ uint16 _actListArrSize;
+ uint16 _alNewscrIndex;
event_t *_freeEvent; // Free list of event structures
event_t *_headEvent; // Head of list (earliest time)
event_t *_tailEvent; // Tail of list (latest time)
+ event_t _events[kMaxEvents]; // Statically declare event structures
+
+ act **_actListArr;
- event_t *getQueue();
- void delQueue(event_t *curEvent);
- event_t *doAction(event_t *curEvent);
-
virtual const char *getCypher() = 0;
+ virtual event_t *doAction(event_t *curEvent) = 0;
+ virtual void delQueue(event_t *curEvent) = 0;
+ virtual void insertAction(act *action) = 0;
+
+ event_t *getQueue();
+
+ uint32 getDosTicks(bool updateFl);
+ uint32 getWinTicks();
+
};
class Scheduler_v1d : public Scheduler {
public:
- Scheduler_v1d(HugoEngine &vm);
+ Scheduler_v1d(HugoEngine *vm);
~Scheduler_v1d();
- const char *getCypher();
+ virtual const char *getCypher();
+
+ virtual uint32 getTicks();
+
+ virtual void insertAction(act *action);
+ virtual void runScheduler();
+protected:
+ virtual void delQueue(event_t *curEvent);
+ virtual event_t *doAction(event_t *curEvent);
+};
+
+class Scheduler_v2d : public Scheduler_v1d {
+public:
+ Scheduler_v2d(HugoEngine *vm);
+ virtual ~Scheduler_v2d();
+
+ virtual const char *getCypher();
+ void insertAction(act *action);
+protected:
+ void delQueue(event_t *curEvent);
+ event_t *doAction(event_t *curEvent);
};
-class Scheduler_v3d : public Scheduler {
+class Scheduler_v3d : public Scheduler_v2d {
public:
- Scheduler_v3d(HugoEngine &vm);
+ Scheduler_v3d(HugoEngine *vm);
~Scheduler_v3d();
const char *getCypher();
};
-} // End of namespace Hugo
+class Scheduler_v1w : public Scheduler_v3d {
+public:
+ Scheduler_v1w(HugoEngine *vm);
+ ~Scheduler_v1w();
+
+ uint32 getTicks();
+ void runScheduler();
+};
+} // End of namespace Hugo
#endif //HUGO_SCHEDULE_H
diff --git a/engines/hugo/schedule_v1d.cpp b/engines/hugo/schedule_v1d.cpp
index 81daf639f6..9098137662 100644
--- a/engines/hugo/schedule_v1d.cpp
+++ b/engines/hugo/schedule_v1d.cpp
@@ -34,19 +34,316 @@
#include "common/system.h"
+#include "hugo/game.h"
#include "hugo/hugo.h"
#include "hugo/schedule.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+#include "hugo/object.h"
namespace Hugo {
-Scheduler_v1d::Scheduler_v1d(HugoEngine &vm) : Scheduler(vm) {
+Scheduler_v1d::Scheduler_v1d(HugoEngine *vm) : Scheduler(vm) {
}
Scheduler_v1d::~Scheduler_v1d() {
}
const char *Scheduler_v1d::getCypher() {
- return "Copyright 1991, Gray Design Associates";
+ return "Copyright (c) 1990, Gray Design Associates";
+}
+
+uint32 Scheduler_v1d::getTicks() {
+ return getDosTicks(false);
+}
+
+/**
+* Delete an event structure (i.e. return it to the free list)
+* Note that event is assumed at head of queue (i.e. earliest). To delete
+* an event from the middle of the queue, merely overwrite its action type
+* to be ANULL
+*/
+void Scheduler_v1d::delQueue(event_t *curEvent) {
+ debugC(4, kDebugSchedule, "delQueue()");
+
+ if (curEvent == _headEvent) // If p was the head ptr
+ _headEvent = curEvent->nextEvent; // then make new head_p
+
+ if (_headEvent)
+ _headEvent->prevEvent = 0; // Mark end of list
+ else
+ _tailEvent = 0; // Empty queue
+
+ curEvent->nextEvent = _freeEvent; // Return p to free list
+ if (_freeEvent) // Special case, if free list was empty
+ _freeEvent->prevEvent = curEvent;
+ _freeEvent = curEvent;
+}
+
+/**
+* This function performs the action in the event structure pointed to by p
+* It dequeues the event and returns it to the free list. It returns a ptr
+* to the next action in the list, except special case of NEW_SCREEN
+*/
+event_t *Scheduler_v1d::doAction(event_t *curEvent) {
+ debugC(1, kDebugSchedule, "doAction - Event action type : %d", curEvent->action->a0.actType);
+
+ status_t &gameStatus = _vm->getGameStatus();
+ act *action = curEvent->action;
+ char *response; // User's response string
+ object_t *obj1;
+ object_t *obj2;
+ int dx, dy;
+ event_t *wrkEvent; // Save ev_p->next_p for return
+
+ switch (action->a0.actType) {
+ case ANULL: // Big NOP from DEL_EVENTS
+ break;
+ case ASCHEDULE: // act0: Schedule an action list
+ insertActionList(action->a0.actIndex);
+ break;
+ case START_OBJ: // act1: Start an object cycling
+ _vm->_object->_objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm->_object->_objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm->_object->_objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm->_object->_objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+ response = Utils::Box(BOX_PROMPT, "%s", _vm->_file->fetchString(action->a3.promptIndex));
+ strcpy(response, _vm->_file->fetchString(action->a3.promptIndex));
+ if (action->a3.encodedFl)
+ decodeString(response);
+
+ warning("STUB: doAction(act3), expecting answer %s", response);
+
+ // TODO: The answer of the player is not handled currently! Once it'll be read in the messageBox, uncomment this block
+#if 0
+ if (strstr (response, action->a3.response))
+ insertActionList(action->a3.actPassIndex);
+ else
+ insertActionList(action->a3.actFailIndex);
+#endif
+
+ // HACK: As the answer is not read, currently it's always considered correct
+ insertActionList(action->a3.actPassIndex);
+ break;
+ }
+ case BKGD_COLOR: // act4: Set new background color
+ _vm->_screen->setBackgroundColor(action->a4.newBackgroundColor);
+ break;
+ case INIT_OBJVXY: // act5: Initialise an object velocity
+ _vm->_object->setVelocity(action->a5.objNumb, action->a5.vx, action->a5.vy);
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm->_object->setCarry(action->a6.objNumb, action->a6.carriedFl); // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm->_object->_objects[action->a7.objNumb].x = _vm->_hero->x - 1;
+ _vm->_object->_objects[action->a7.objNumb].y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->_object->_objects[action->a7.objNumb].screenIndex = *_vm->_screen_p; // Don't forget screen!
+ break;
+ case NEW_SCREEN: // act8: Start new screen
+ newScreen(action->a8.screenIndex);
+ break;
+ case INIT_OBJSTATE: // act9: Initialise an object state
+ _vm->_object->_objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm->_object->setPath(action->a10.objNumb, (path_t) action->a10.newPathType, action->a10.vxPath, action->a10.vyPath);
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm->_object->_objects[action->a11.objNumb].state == action->a11.stateReq)
+ insertActionList(action->a11.actPassIndex);
+ else
+ insertActionList(action->a11.actFailIndex);
+ break;
+ case TEXT: // act12: Text box (CF WARN)
+ Utils::Box(BOX_ANY, "%s", _vm->_file->fetchString(action->a12.stringIndex)); // Fetch string from file
+ break;
+ case SWAP_IMAGES: // act13: Swap 2 object images
+ _vm->_object->swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm->_object->_objects[action->a14.objNumb].screenIndex == action->a14.screenReq)
+ insertActionList(action->a14.actPassIndex);
+ else
+ insertActionList(action->a14.actFailIndex);
+ break;
+ case AUTOPILOT: // act15: Home in on a (stationary) object
+ // object p1 will home in on object p2
+ obj1 = &_vm->_object->_objects[action->a15.obj1];
+ obj2 = &_vm->_object->_objects[action->a15.obj2];
+ obj1->pathType = AUTO;
+ dx = obj1->x + obj1->currImagePtr->x1 - obj2->x - obj2->currImagePtr->x1;
+ dy = obj1->y + obj1->currImagePtr->y1 - obj2->y - obj2->currImagePtr->y1;
+
+ if (dx == 0) // Don't EVER divide by zero!
+ dx = 1;
+ if (dy == 0)
+ dy = 1;
+
+ if (abs(dx) > abs(dy)) {
+ obj1->vx = action->a15.dx * -SIGN(dx);
+ obj1->vy = abs((action->a15.dy * dy) / dx) * -SIGN(dy);
+ } else {
+ obj1->vy = action->a15.dy * SIGN(dy);
+ obj1->vx = abs((action->a15.dx * dx) / dy) * SIGN(dx);
+ }
+ break;
+ case INIT_OBJ_SEQ: // act16: Set sequence number to use
+ // Note: Don't set a sequence at time 0 of a new screen, it causes
+ // problems clearing the boundary bits of the object! t>0 is safe
+ _vm->_object->_objects[action->a16.objNumb].currImagePtr = _vm->_object->_objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm->_object->_objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm->_object->_objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm->_object->_objects[action->a19.objNumb].state & action->a19.stateMask) == action->a19.stateMask)
+ insertActionList(action->a19.actPassIndex);
+ else
+ insertActionList(action->a19.actFailIndex);
+ break;
+ case DEL_EVENTS: // act20: Remove all events of this action type
+ // Note: actions are not deleted here, simply turned into NOPs!
+ wrkEvent = _headEvent; // The earliest event
+ while (wrkEvent) { // While events found in list
+ if (wrkEvent->action->a20.actType == action->a20.actTypeDel)
+ wrkEvent->action->a20.actType = ANULL;
+ wrkEvent = wrkEvent->nextEvent;
+ }
+ break;
+ case GAMEOVER: // act21: Game over!
+ // NOTE: Must wait at least 1 tick before issuing this action if
+ // any objects are to be made invisible!
+ gameStatus.gameOverFl = true;
+ break;
+ case INIT_HH_COORD: // act22: Initialise an object to hero's actual coords
+ _vm->_object->_objects[action->a22.objNumb].x = _vm->_hero->x;
+ _vm->_object->_objects[action->a22.objNumb].y = _vm->_hero->y;
+ _vm->_object->_objects[action->a22.objNumb].screenIndex = *_vm->_screen_p;// Don't forget screen!
+ break;
+ case EXIT: // act23: Exit game back to DOS
+ _vm->endGame();
+ break;
+ case BONUS: // act24: Get bonus score for action
+ processBonus(action->a24.pointIndex);
+ break;
+ case COND_BOX: // act25: Conditional on bounding box
+ obj1 = &_vm->_object->_objects[action->a25.objNumb];
+ dx = obj1->x + obj1->currImagePtr->x1;
+ dy = obj1->y + obj1->currImagePtr->y2;
+ if ((dx >= action->a25.x1) && (dx <= action->a25.x2) &&
+ (dy >= action->a25.y1) && (dy <= action->a25.y2))
+ insertActionList(action->a25.actPassIndex);
+ else
+ insertActionList(action->a25.actFailIndex);
+ break;
+// case SOUND: // act26: Play a sound (or tune)
+// if (action->a26.soundIndex < _vm->_tunesNbr)
+// _vm->_sound->playMusic(action->a26.soundIndex);
+// else
+// _vm->_sound->playSound(action->a26.soundIndex, BOTH_CHANNELS, MED_PRI);
+// break;
+ case ADD_SCORE: // act27: Add object's value to score
+ _vm->adjustScore(_vm->_object->_objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm->adjustScore(-_vm->_object->_objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm->_object->isCarried(action->a29.objNumb))
+ insertActionList(action->a29.actPassIndex);
+ else
+ insertActionList(action->a29.actFailIndex);
+ break;
+ case OLD_SONG:
+ //TODO For Hugo 1 and Hugo2 DOS: The songs were not stored in a DAT file, but directly as
+ //strings. the current play_music should be modified to use a strings instead of reading
+ //the file, in those cases. This replaces, for those DOS versions, act26.
+ warning("STUB: doAction(act49)");
+ break;
+ default:
+ error("An error has occurred: %s", "doAction");
+ break;
+ }
+
+ if (action->a0.actType == NEW_SCREEN) { // New_screen() deletes entire list
+ return 0; // next_p = 0 since list now empty
+ } else {
+ wrkEvent = curEvent->nextEvent;
+ delQueue(curEvent); // Return event to free list
+ return wrkEvent; // Return next event ptr
+ }
+}
+
+/**
+* Insert the action pointed to by p into the timer event queue
+* The queue goes from head (earliest) to tail (latest) timewise
+*/
+void Scheduler_v1d::insertAction(act *action) {
+ debugC(1, kDebugSchedule, "insertAction() - Action type A%d", action->a0.actType);
+
+ // First, get and initialise the event structure
+ event_t *curEvent = getQueue();
+ curEvent->action = action;
+
+ curEvent->localActionFl = true; // Rest are for current screen only
+
+ curEvent->time = action->a0.timer + getTicks(); // Convert rel to abs time
+
+ // Now find the place to insert the event
+ if (!_tailEvent) { // Empty queue
+ _tailEvent = _headEvent = curEvent;
+ curEvent->nextEvent = curEvent->prevEvent = 0;
+ } else {
+ event_t *wrkEvent = _tailEvent; // Search from latest time back
+ bool found = false;
+
+ while (wrkEvent && !found) {
+ if (wrkEvent->time <= curEvent->time) { // Found if new event later
+ found = true;
+ if (wrkEvent == _tailEvent) // New latest in list
+ _tailEvent = curEvent;
+ else
+ wrkEvent->nextEvent->prevEvent = curEvent;
+ curEvent->nextEvent = wrkEvent->nextEvent;
+ wrkEvent->nextEvent = curEvent;
+ curEvent->prevEvent = wrkEvent;
+ }
+ wrkEvent = wrkEvent->prevEvent;
+ }
+
+ if (!found) { // Must be earliest in list
+ _headEvent->prevEvent = curEvent; // So insert as new head
+ curEvent->nextEvent = _headEvent;
+ curEvent->prevEvent = 0;
+ _headEvent = curEvent;
+ }
+ }
+}
+
+/**
+* This is the scheduler which runs every tick. It examines the event queue
+* for any events whose time has come. It dequeues these events and performs
+* the action associated with the event, returning it to the free queue
+*/
+void Scheduler_v1d::runScheduler() {
+ debugC(6, kDebugSchedule, "runScheduler");
+
+ uint32 ticker = getTicks(); // The time now, in ticks
+ event_t *curEvent = _headEvent; // The earliest event
+
+ while (curEvent && (curEvent->time <= ticker)) // While mature events found
+ curEvent = doAction(curEvent); // Perform the action (returns next_p)
}
} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v1w.cpp b/engines/hugo/schedule_v1w.cpp
new file mode 100644
index 0000000000..1cb9d4b42b
--- /dev/null
+++ b/engines/hugo/schedule_v1w.cpp
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// This module contains all the scheduling and timing stuff
+
+#include "common/system.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/schedule.h"
+#include "hugo/global.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+#include "hugo/object.h"
+
+namespace Hugo {
+
+Scheduler_v1w::Scheduler_v1w(HugoEngine *vm) : Scheduler_v3d(vm) {
+}
+
+Scheduler_v1w::~Scheduler_v1w() {
+}
+
+uint32 Scheduler_v1w::getTicks() {
+ return getWinTicks();
+}
+
+/**
+* This is the scheduler which runs every tick. It examines the event queue
+* for any events whose time has come. It dequeues these events and performs
+* the action associated with the event, returning it to the free queue
+*/
+void Scheduler_v1w::runScheduler() {
+ debugC(6, kDebugSchedule, "runScheduler");
+
+ uint32 ticker = getTicks(); // The time now, in ticks
+ event_t *curEvent = _headEvent; // The earliest event
+
+ while (curEvent && (curEvent->time <= ticker)) // While mature events found
+ curEvent = doAction(curEvent); // Perform the action (returns next_p)
+
+ _vm->getGameStatus().tick++; // Accessed elsewhere via getTicks()
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v2d.cpp b/engines/hugo/schedule_v2d.cpp
new file mode 100644
index 0000000000..7e8c0cd900
--- /dev/null
+++ b/engines/hugo/schedule_v2d.cpp
@@ -0,0 +1,450 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// This module contains all the scheduling and timing stuff
+
+#include "common/system.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/schedule.h"
+#include "hugo/global.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+#include "hugo/object.h"
+
+namespace Hugo {
+
+Scheduler_v2d::Scheduler_v2d(HugoEngine *vm) : Scheduler_v1d(vm) {
+}
+
+Scheduler_v2d::~Scheduler_v2d() {
+}
+
+const char *Scheduler_v2d::getCypher() {
+ return "Copyright 1991, Gray Design Associates";
+}
+
+/**
+* Delete an event structure (i.e. return it to the free list)
+* Historical note: Originally event p was assumed to be at head of queue
+* (i.e. earliest) since all events were deleted in order when proceeding to
+* a new screen. To delete an event from the middle of the queue, the action
+* was overwritten to be ANULL. With the advent of GLOBAL events, delQueue
+* was modified to allow deletes anywhere in the list, and the DEL_EVENT
+* action was modified to perform the actual delete.
+*/
+void Scheduler_v2d::delQueue(event_t *curEvent) {
+ debugC(4, kDebugSchedule, "delQueue()");
+
+ if (curEvent == _headEvent) { // If p was the head ptr
+ _headEvent = curEvent->nextEvent; // then make new head_p
+ } else { // Unlink p
+ curEvent->prevEvent->nextEvent = curEvent->nextEvent;
+ if (curEvent->nextEvent)
+ curEvent->nextEvent->prevEvent = curEvent->prevEvent;
+ else
+ _tailEvent = curEvent->prevEvent;
+ }
+
+ if (_headEvent)
+ _headEvent->prevEvent = 0; // Mark end of list
+ else
+ _tailEvent = 0; // Empty queue
+
+ curEvent->nextEvent = _freeEvent; // Return p to free list
+ if (_freeEvent) // Special case, if free list was empty
+ _freeEvent->prevEvent = curEvent;
+ _freeEvent = curEvent;
+}
+
+/**
+* This function performs the action in the event structure pointed to by p
+* It dequeues the event and returns it to the free list. It returns a ptr
+* to the next action in the list, except special case of NEW_SCREEN
+*/
+event_t *Scheduler_v2d::doAction(event_t *curEvent) {
+ debugC(1, kDebugSchedule, "doAction - Event action type : %d", curEvent->action->a0.actType);
+
+ status_t &gameStatus = _vm->getGameStatus();
+ act *action = curEvent->action;
+ char *response; // User's response string
+ object_t *obj1;
+ object_t *obj2;
+ int dx, dy;
+ event_t *wrkEvent; // Save ev_p->next_p for return
+ event_t *saveEvent; // Used in DEL_EVENTS
+
+ switch (action->a0.actType) {
+ case ANULL: // Big NOP from DEL_EVENTS
+ break;
+ case ASCHEDULE: // act0: Schedule an action list
+ insertActionList(action->a0.actIndex);
+ break;
+ case START_OBJ: // act1: Start an object cycling
+ _vm->_object->_objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm->_object->_objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm->_object->_objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm->_object->_objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+ response = Utils::Box(BOX_PROMPT, "%s", _vm->_file->fetchString(action->a3.promptIndex));
+
+ warning("STUB: doAction(act3), expecting answer %s", _vm->_file->fetchString(action->a3.responsePtr[0]));
+
+ // TODO: The answer of the player is not handled currently! Once it'll be read in the messageBox, uncomment this block
+#if 0
+ bool found;
+ char *tmpStr; // General purpose string ptr
+
+ for (found = false, dx = 0; !found && (action->a3.responsePtr[dx] != -1); dx++) {
+ tmpStr = _vm->_file->fetchString(action->a3.responsePtr[dx]);
+ if (strstr(Utils::strlwr(response) , tmpStr))
+ found = true;
+ }
+
+ if (found)
+ insertActionList(action->a3.actPassIndex);
+ else
+ insertActionList(action->a3.actFailIndex);
+#endif
+
+ // HACK: As the answer is not read, currently it's always considered correct
+ insertActionList(action->a3.actPassIndex);
+ break;
+ }
+ case BKGD_COLOR: // act4: Set new background color
+ _vm->_screen->setBackgroundColor(action->a4.newBackgroundColor);
+ break;
+ case INIT_OBJVXY: // act5: Initialise an object velocity
+ _vm->_object->setVelocity(action->a5.objNumb, action->a5.vx, action->a5.vy);
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm->_object->setCarry(action->a6.objNumb, action->a6.carriedFl); // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm->_object->_objects[action->a7.objNumb].x = _vm->_hero->x - 1;
+ _vm->_object->_objects[action->a7.objNumb].y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->_object->_objects[action->a7.objNumb].screenIndex = *_vm->_screen_p; // Don't forget screen!
+ break;
+ case NEW_SCREEN: // act8: Start new screen
+ newScreen(action->a8.screenIndex);
+ break;
+ case INIT_OBJSTATE: // act9: Initialise an object state
+ _vm->_object->_objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm->_object->setPath(action->a10.objNumb, (path_t) action->a10.newPathType, action->a10.vxPath, action->a10.vyPath);
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm->_object->_objects[action->a11.objNumb].state == action->a11.stateReq)
+ insertActionList(action->a11.actPassIndex);
+ else
+ insertActionList(action->a11.actFailIndex);
+ break;
+ case TEXT: // act12: Text box (CF WARN)
+ Utils::Box(BOX_ANY, "%s", _vm->_file->fetchString(action->a12.stringIndex)); // Fetch string from file
+ break;
+ case SWAP_IMAGES: // act13: Swap 2 object images
+ _vm->_object->swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm->_object->_objects[action->a14.objNumb].screenIndex == action->a14.screenReq)
+ insertActionList(action->a14.actPassIndex);
+ else
+ insertActionList(action->a14.actFailIndex);
+ break;
+ case AUTOPILOT: // act15: Home in on a (stationary) object
+ // object p1 will home in on object p2
+ obj1 = &_vm->_object->_objects[action->a15.obj1];
+ obj2 = &_vm->_object->_objects[action->a15.obj2];
+ obj1->pathType = AUTO;
+ dx = obj1->x + obj1->currImagePtr->x1 - obj2->x - obj2->currImagePtr->x1;
+ dy = obj1->y + obj1->currImagePtr->y1 - obj2->y - obj2->currImagePtr->y1;
+
+ if (dx == 0) // Don't EVER divide by zero!
+ dx = 1;
+ if (dy == 0)
+ dy = 1;
+
+ if (abs(dx) > abs(dy)) {
+ obj1->vx = action->a15.dx * -SIGN(dx);
+ obj1->vy = abs((action->a15.dy * dy) / dx) * -SIGN(dy);
+ } else {
+ obj1->vy = action->a15.dy * -SIGN(dy);
+ obj1->vx = abs((action->a15.dx * dx) / dy) * -SIGN(dx);
+ }
+ break;
+ case INIT_OBJ_SEQ: // act16: Set sequence number to use
+ // Note: Don't set a sequence at time 0 of a new screen, it causes
+ // problems clearing the boundary bits of the object! t>0 is safe
+ _vm->_object->_objects[action->a16.objNumb].currImagePtr = _vm->_object->_objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm->_object->_objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm->_object->_objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm->_object->_objects[action->a19.objNumb].state & action->a19.stateMask) == action->a19.stateMask)
+ insertActionList(action->a19.actPassIndex);
+ else
+ insertActionList(action->a19.actFailIndex);
+ break;
+ case DEL_EVENTS: // act20: Remove all events of this action type
+ // Note: actions are not deleted here, simply turned into NOPs!
+ wrkEvent = _headEvent; // The earliest event
+ while (wrkEvent) { // While events found in list
+ saveEvent = wrkEvent->nextEvent;
+ if (wrkEvent->action->a20.actType == action->a20.actTypeDel)
+ delQueue(wrkEvent);
+ wrkEvent = saveEvent;
+ }
+ break;
+ case GAMEOVER: // act21: Game over!
+ // NOTE: Must wait at least 1 tick before issuing this action if
+ // any objects are to be made invisible!
+ gameStatus.gameOverFl = true;
+ break;
+ case INIT_HH_COORD: // act22: Initialise an object to hero's actual coords
+ _vm->_object->_objects[action->a22.objNumb].x = _vm->_hero->x;
+ _vm->_object->_objects[action->a22.objNumb].y = _vm->_hero->y;
+ _vm->_object->_objects[action->a22.objNumb].screenIndex = *_vm->_screen_p;// Don't forget screen!
+ break;
+ case EXIT: // act23: Exit game back to DOS
+ _vm->endGame();
+ break;
+ case BONUS: // act24: Get bonus score for action
+ processBonus(action->a24.pointIndex);
+ break;
+ case COND_BOX: // act25: Conditional on bounding box
+ obj1 = &_vm->_object->_objects[action->a25.objNumb];
+ dx = obj1->x + obj1->currImagePtr->x1;
+ dy = obj1->y + obj1->currImagePtr->y2;
+ if ((dx >= action->a25.x1) && (dx <= action->a25.x2) &&
+ (dy >= action->a25.y1) && (dy <= action->a25.y2))
+ insertActionList(action->a25.actPassIndex);
+ else
+ insertActionList(action->a25.actFailIndex);
+ break;
+ case SOUND: // act26: Play a sound (or tune)
+ if (action->a26.soundIndex < _vm->_tunesNbr)
+ _vm->_sound->playMusic(action->a26.soundIndex);
+ else
+ _vm->_sound->playSound(action->a26.soundIndex, BOTH_CHANNELS, MED_PRI);
+ break;
+ case ADD_SCORE: // act27: Add object's value to score
+ _vm->adjustScore(_vm->_object->_objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm->adjustScore(-_vm->_object->_objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm->_object->isCarried(action->a29.objNumb))
+ insertActionList(action->a29.actPassIndex);
+ else
+ insertActionList(action->a29.actFailIndex);
+ break;
+ case INIT_MAZE: // act30: Enable and init maze structure
+ _maze.enabledFl = true;
+ _maze.size = action->a30.mazeSize;
+ _maze.x1 = action->a30.x1;
+ _maze.y1 = action->a30.y1;
+ _maze.x2 = action->a30.x2;
+ _maze.y2 = action->a30.y2;
+ _maze.x3 = action->a30.x3;
+ _maze.x4 = action->a30.x4;
+ _maze.firstScreenIndex = action->a30.firstScreenIndex;
+ break;
+ case EXIT_MAZE: // act31: Disable maze mode
+ _maze.enabledFl = false;
+ break;
+ case INIT_PRIORITY:
+ _vm->_object->_objects[action->a32.objNumb].priority = action->a32.priority;
+ break;
+ case INIT_SCREEN:
+ _vm->_object->_objects[action->a33.objNumb].screenIndex = action->a33.screenIndex;
+ break;
+ case AGSCHEDULE: // act34: Schedule a (global) action list
+ insertActionList(action->a34.actIndex);
+ break;
+ case REMAPPAL: // act35: Remap a palette color
+ _vm->_screen->remapPal(action->a35.oldColorIndex, action->a35.newColorIndex);
+ break;
+ case COND_NOUN: // act36: Conditional on noun mentioned
+ if (_vm->_parser->isWordPresent(_vm->_arrayNouns[action->a36.nounIndex]))
+ insertActionList(action->a36.actPassIndex);
+ else
+ insertActionList(action->a36.actFailIndex);
+ break;
+ case SCREEN_STATE: // act37: Set new screen state
+ _vm->_screenStates[action->a37.screenIndex] = action->a37.newState;
+ break;
+ case INIT_LIPS: // act38: Position lips on object
+ _vm->_object->_objects[action->a38.lipsObjNumb].x = _vm->_object->_objects[action->a38.objNumb].x + action->a38.dxLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].y = _vm->_object->_objects[action->a38.objNumb].y + action->a38.dyLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].screenIndex = *_vm->_screen_p; // Don't forget screen!
+ _vm->_object->_objects[action->a38.lipsObjNumb].cycling = CYCLE_FORWARD;
+ break;
+ case INIT_STORY_MODE: // act39: Init story_mode flag
+ // This is similar to the QUIET path mode, except that it is
+ // independant of it and it additionally disables the ">" prompt
+ gameStatus.storyModeFl = action->a39.storyModeFl;
+
+ // End the game after story if this is special vendor demo mode
+ if (gameStatus.demoFl && action->a39.storyModeFl == false)
+ _vm->endGame();
+ break;
+ case WARN: // act40: Text box (CF TEXT)
+ Utils::Box(BOX_OK, "%s", _vm->_file->fetchString(action->a40.stringIndex));
+ break;
+ case COND_BONUS: // act41: Perform action if got bonus
+ if (_vm->_points[action->a41.BonusIndex].scoredFl)
+ insertActionList(action->a41.actPassIndex);
+ else
+ insertActionList(action->a41.actFailIndex);
+ break;
+ case TEXT_TAKE: // act42: Text box with "take" message
+ Utils::Box(BOX_ANY, TAKE_TEXT, _vm->_arrayNouns[_vm->_object->_objects[action->a42.objNumb].nounIndex][TAKE_NAME]);
+ break;
+ case YESNO: // act43: Prompt user for Yes or No
+ warning("doAction(act43) - Yes/No Box");
+ if (Utils::Box(BOX_YESNO, "%s", _vm->_file->fetchString(action->a43.promptIndex)) != 0)
+ insertActionList(action->a43.actYesIndex);
+ else
+ insertActionList(action->a43.actNoIndex);
+ break;
+ case STOP_ROUTE: // act44: Stop any route in progress
+ gameStatus.routeIndex = -1;
+ break;
+ case COND_ROUTE: // act45: Conditional on route in progress
+ if (gameStatus.routeIndex >= action->a45.routeIndex)
+ insertActionList(action->a45.actPassIndex);
+ else
+ insertActionList(action->a45.actFailIndex);
+ break;
+ case INIT_JUMPEXIT: // act46: Init status.jumpexit flag
+ // This is to allow left click on exit to get there immediately
+ // For example the plane crash in Hugo2 where hero is invisible
+ // Couldn't use INVISIBLE flag since conflicts with boat in Hugo1
+ gameStatus.jumpExitFl = action->a46.jumpExitFl;
+ break;
+ case INIT_VIEW: // act47: Init object.viewx, viewy, dir
+ _vm->_object->_objects[action->a47.objNumb].viewx = action->a47.viewx;
+ _vm->_object->_objects[action->a47.objNumb].viewy = action->a47.viewy;
+ _vm->_object->_objects[action->a47.objNumb].direction = action->a47.direction;
+ break;
+ case INIT_OBJ_FRAME: // act48: Set seq,frame number to use
+ // Note: Don't set a sequence at time 0 of a new screen, it causes
+ // problems clearing the boundary bits of the object! t>0 is safe
+ _vm->_object->_objects[action->a48.objNumb].currImagePtr = _vm->_object->_objects[action->a48.objNumb].seqList[action->a48.seqIndex].seqPtr;
+ for (dx = 0; dx < action->a48.frameIndex; dx++)
+ _vm->_object->_objects[action->a48.objNumb].currImagePtr = _vm->_object->_objects[action->a48.objNumb].currImagePtr->nextSeqPtr;
+ break;
+ case OLD_SONG:
+ //TODO For Hugo 1 and Hugo2 DOS: The songs were not stored in a DAT file, but directly as
+ //strings. the current play_music should be modified to use a strings instead of reading
+ //the file, in those cases. This replaces, for those DOS versions, act26.
+ warning("STUB: doAction(act49)");
+ break;
+ default:
+ error("An error has occurred: %s", "doAction");
+ break;
+ }
+
+ if (action->a0.actType == NEW_SCREEN) { // New_screen() deletes entire list
+ return 0; // next_p = 0 since list now empty
+ } else {
+ wrkEvent = curEvent->nextEvent;
+ delQueue(curEvent); // Return event to free list
+ return wrkEvent; // Return next event ptr
+ }
+}
+
+/**
+* Insert the action pointed to by p into the timer event queue
+* The queue goes from head (earliest) to tail (latest) timewise
+*/
+void Scheduler_v2d::insertAction(act *action) {
+ debugC(1, kDebugSchedule, "insertAction() - Action type A%d", action->a0.actType);
+
+ // First, get and initialise the event structure
+ event_t *curEvent = getQueue();
+ curEvent->action = action;
+ switch (action->a0.actType) { // Assign whether local or global
+ case AGSCHEDULE:
+ curEvent->localActionFl = false; // Lasts over a new screen
+ break;
+ default:
+ curEvent->localActionFl = true; // Rest are for current screen only
+ break;
+ }
+
+ curEvent->time = action->a0.timer + getTicks(); // Convert rel to abs time
+
+ // Now find the place to insert the event
+ if (!_tailEvent) { // Empty queue
+ _tailEvent = _headEvent = curEvent;
+ curEvent->nextEvent = curEvent->prevEvent = 0;
+ } else {
+ event_t *wrkEvent = _tailEvent; // Search from latest time back
+ bool found = false;
+
+ while (wrkEvent && !found) {
+ if (wrkEvent->time <= curEvent->time) { // Found if new event later
+ found = true;
+ if (wrkEvent == _tailEvent) // New latest in list
+ _tailEvent = curEvent;
+ else
+ wrkEvent->nextEvent->prevEvent = curEvent;
+ curEvent->nextEvent = wrkEvent->nextEvent;
+ wrkEvent->nextEvent = curEvent;
+ curEvent->prevEvent = wrkEvent;
+ }
+ wrkEvent = wrkEvent->prevEvent;
+ }
+
+ if (!found) { // Must be earliest in list
+ _headEvent->prevEvent = curEvent; // So insert as new head
+ curEvent->nextEvent = _headEvent;
+ curEvent->prevEvent = 0;
+ _headEvent = curEvent;
+ }
+ }
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v3d.cpp b/engines/hugo/schedule_v3d.cpp
index 9421b0f5f9..b58f31be74 100644
--- a/engines/hugo/schedule_v3d.cpp
+++ b/engines/hugo/schedule_v3d.cpp
@@ -34,18 +34,27 @@
#include "common/system.h"
+#include "hugo/game.h"
#include "hugo/hugo.h"
#include "hugo/schedule.h"
+#include "hugo/global.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+#include "hugo/object.h"
namespace Hugo {
-Scheduler_v3d::Scheduler_v3d(HugoEngine &vm) : Scheduler(vm) {
+Scheduler_v3d::Scheduler_v3d(HugoEngine *vm) : Scheduler_v2d(vm) {
}
Scheduler_v3d::~Scheduler_v3d() {
}
const char *Scheduler_v3d::getCypher() {
- return "Copyright 1992, Gray Design Associates";
+ return "Copyright 1992, Gray Design Associates";
}
+
} // End of namespace Hugo
diff --git a/engines/hugo/sound.cpp b/engines/hugo/sound.cpp
index e5f55afcc8..a680e00b82 100644
--- a/engines/hugo/sound.cpp
+++ b/engines/hugo/sound.cpp
@@ -188,7 +188,8 @@ void MidiPlayer::close() {
_driver->close();
delete _driver;
_driver = 0;
- _parser->setMidiDriver(0);
+ if (_parser)
+ _parser->setMidiDriver(0);
delete _parser;
_mutex.unlock();
}
@@ -239,22 +240,26 @@ void MidiPlayer::timerCallback(void *p) {
player->updateTimer();
}
-SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) {
+SoundHandler::SoundHandler(HugoEngine *vm) : _vm(vm) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
MidiDriver *driver = MidiDriver::createMidi(dev);
_midiPlayer = new MidiPlayer(driver);
}
+SoundHandler::~SoundHandler() {
+ delete _midiPlayer;
+}
+
void SoundHandler::setMusicVolume() {
/* Set the FM music volume from config.mvolume (0..100%) */
-
+
_midiPlayer->setVolume(_config.musicVolume * 255 / 100);
}
void SoundHandler::stopSound() {
/* Stop any sound that might be playing */
- _vm._mixer->stopAll();
+ _vm->_mixer->stopAll();
}
void SoundHandler::stopMusic() {
@@ -265,7 +270,7 @@ void SoundHandler::stopMusic() {
void SoundHandler::toggleMusic() {
// Turn music on and off
_config.musicFl = !_config.musicFl;
-
+
_midiPlayer->pause(_config.musicFl);
}
@@ -285,8 +290,8 @@ void SoundHandler::playMusic(int16 tune) {
uint16 size; // Size of sequence data
if (_config.musicFl) {
- _vm.getGameStatus().song = tune;
- seqPtr = _vm.file().getSound(tune, &size);
+ _vm->getGameStatus().song = tune;
+ seqPtr = _vm->_file->getSound(tune, &size);
playMIDI(seqPtr, size);
}
}
@@ -302,7 +307,7 @@ void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
static byte curPriority = 0; // Priority of currently playing sound
//
/* Sound disabled */
- if (!_config.soundFl || !_vm._mixer->isReady())
+ if (!_config.soundFl || !_vm->_mixer->isReady())
return;
//
// // See if last wave still playing - if so, check priority
@@ -314,11 +319,11 @@ void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
curPriority = priority;
//
/* Get sound data */
- if ((sound_p = _vm.file().getSound(sound, &size)) == 0)
+ if ((sound_p = _vm->_file->getSound(sound, &size)) == 0)
return;
Audio::AudioStream *stream = Audio::makeRawStream(sound_p, size, 11025, Audio::FLAG_UNSIGNED);
- _vm._mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
+ _vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
}
diff --git a/engines/hugo/sound.h b/engines/hugo/sound.h
index 53a5912a92..202e497215 100644
--- a/engines/hugo/sound.h
+++ b/engines/hugo/sound.h
@@ -38,20 +38,21 @@
namespace Hugo {
class MidiPlayer;
-
+
class SoundHandler {
public:
- SoundHandler(HugoEngine &vm);
+ SoundHandler(HugoEngine *vm);
+ ~SoundHandler();
void toggleMusic();
void toggleSound();
void setMusicVolume();
- void playMusic(short tune);
- void playSound(short sound, stereo_t channel, byte priority);
+ void playMusic(int16 tune);
+ void playSound(int16 sound, stereo_t channel, byte priority);
void initSound();
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
Audio::SoundHandle _soundHandle;
MidiPlayer *_midiPlayer;
diff --git a/engines/hugo/util.cpp b/engines/hugo/util.cpp
index 8cca4b7d84..bad97d80c5 100644
--- a/engines/hugo/util.cpp
+++ b/engines/hugo/util.cpp
@@ -40,8 +40,10 @@
namespace Hugo {
+/**
+ * Returns index (0 to 7) of first 1 in supplied byte, or 8 if not found
+ */
int Utils::firstBit(byte data) {
-// Returns index (0 to 7) of first 1 in supplied byte, or 8 if not found
if (!data)
return 8;
@@ -54,8 +56,10 @@ int Utils::firstBit(byte data) {
return i;
}
+/**
+ * Returns index (0 to 7) of last 1 in supplied byte, or 8 if not found
+ */
int Utils::lastBit(byte data) {
-// Returns index (0 to 7) of last 1 in supplied byte, or 8 if not found
if (!data)
return 8;
@@ -68,8 +72,10 @@ int Utils::lastBit(byte data) {
return i;
}
+/**
+ * Reverse the bit order in supplied byte
+ */
void Utils::reverseByte(byte *data) {
-// Reverse the bit order in supplied byte
byte maskIn = 0x80;
byte maskOut = 0x01;
byte result = 0;
@@ -78,21 +84,21 @@ void Utils::reverseByte(byte *data) {
if (*data & maskIn)
result |= maskOut;
}
-
+
*data = result;
}
char *Utils::Box(box_t dismiss, const char *s, ...) {
static char buffer[MAX_STRLEN + 1]; // Format text into this
- if (!s)
+ if (!s)
return 0; // NULL strings catered for
if (s[0] == '\0')
return 0;
if (strlen(s) > MAX_STRLEN - 100) { // Test length
- Warn("String too big:\n%s", s);
+ warning("String too big: '%s'", s);
return 0;
}
@@ -129,62 +135,10 @@ char *Utils::Box(box_t dismiss, const char *s, ...) {
return buffer;
}
-void Utils::Warn(const char *format, ...) {
-// Warning handler. Print supplied message and continue
-// Arguments are same as printf
- char buffer[WARNLEN];
- va_list marker;
- va_start(marker, format);
- vsnprintf(buffer, WARNLEN, format, marker);
- va_end(marker);
- warning("Hugo warning: %s", buffer);
-}
-
-void Utils::Error(int error_type, const char *format, ...) {
-// Fatal error handler. Reset environment, print error and exit
-// Arguments are same as printf
- char buffer[ERRLEN + 1];
- bool fatal = true; // Fatal error, else continue
-
- switch (error_type) {
- case FILE_ERR:
- strcpy(buffer, HugoEngine::get()._textUtil[kErr1]);
- break;
- case WRITE_ERR:
- strcpy(buffer, HugoEngine::get()._textUtil[kErr2]);
- fatal = false; // Allow continuation
- break;
- case PCCH_ERR:
- strcpy(buffer, HugoEngine::get()._textUtil[kErr3]);
- break;
- case HEAP_ERR:
- strcpy(buffer, HugoEngine::get()._textUtil[kErr4]);
- break;
- case SOUND_ERR:
- strcpy(buffer, HugoEngine::get()._textUtil[kErr5]);
- break;
- default:
- strcpy(buffer, HugoEngine::get()._textUtil[kErr6]);
- break;
- }
-
- if (fatal)
- HugoEngine::get().shutdown(); // Restore any devices before exit
-
- va_list marker;
- va_start(marker, format);
- vsnprintf(&buffer[strlen(buffer)], ERRLEN - strlen(buffer), format, marker);
- va_end(marker);
- //MessageBeep(MB_ICONEXCLAMATION);
- //MessageBox(hwnd, buffer, "HugoWin Error", MB_OK | MB_ICONEXCLAMATION);
- warning("Hugo Error: %s", buffer);
-
- if (fatal)
- exit(1);
-}
-
+/**
+ * Print options for user when dead
+ */
void Utils::gameOverMsg(void) {
- // Print options for user when dead
//MessageBox(hwnd, gameoverstring, "Be more careful next time!", MB_OK | MB_ICONINFORMATION);
warning("STUB: Gameover_msg(): %s", HugoEngine::get()._textUtil[kGameOver]);
}
@@ -201,5 +155,4 @@ char *Utils::strlwr(char *buffer) {
return result;
}
-
} // End of namespace Hugo
diff --git a/engines/hugo/util.h b/engines/hugo/util.h
index 69428fb3bb..4cf30b1903 100644
--- a/engines/hugo/util.h
+++ b/engines/hugo/util.h
@@ -36,16 +36,7 @@
namespace Hugo {
enum seqTextUtil {
- kTech = 0,
- kErr1 = 1,
- kErr2 = 2,
- kErr3 = 3,
- kErr4 = 4,
- kErr5 = 5,
- kErr6 = 6,
- kGameOver = 7
-// kObsoleteErr1 = 8,
-// kObsoleteErr2 = 9
+ kGameOver = 0
};
namespace Utils {
@@ -54,8 +45,6 @@ int lastBit(byte data);
void gameOverMsg();
void reverseByte(byte *data);
-void Error(int code, const char *format, ...) GCC_PRINTF(2, 3);
-void Warn(const char *format, ...) GCC_PRINTF(1, 2);
char *Box(box_t, const char *, ...) GCC_PRINTF(2, 3);
char *strlwr(char *buffer);
diff --git a/engines/kyra/animator_v2.cpp b/engines/kyra/animator_v2.cpp
index 6c4fafa674..b06dffd36f 100644
--- a/engines/kyra/animator_v2.cpp
+++ b/engines/kyra/animator_v2.cpp
@@ -141,7 +141,7 @@ void KyraEngine_v2::flagAnimObjsSpecialRefresh() {
}
void KyraEngine_v2::addItemToAnimList(int item) {
- assert(item < _itemListSize);
+ assert(item >= 0 && item < _itemListSize);
restorePage3();
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp
index fc509700d7..d58494303c 100644
--- a/engines/kyra/debugger.cpp
+++ b/engines/kyra/debugger.cpp
@@ -53,6 +53,16 @@ Debugger::Debugger(KyraEngine_v1 *vm)
DCmd_Register("settimercountdown", WRAP_METHOD(Debugger, cmd_setTimerCountdown));
}
+void Debugger::preEnter() {
+ _vm->pauseEngine(true);
+ ::GUI::Debugger::preEnter();
+}
+
+void Debugger::postEnter() {
+ ::GUI::Debugger::postEnter();
+ _vm->pauseEngine(false);
+}
+
bool Debugger::cmd_setScreenDebug(int argc, const char **argv) {
if (argc > 1) {
if (scumm_stricmp(argv[1], "enable") == 0)
@@ -201,14 +211,6 @@ Debugger_LoK::Debugger_LoK(KyraEngine_LoK *vm)
DCmd_Register("birthstones", WRAP_METHOD(Debugger_LoK, cmd_listBirthstones));
}
-void Debugger_LoK::preEnter() {
- //_vm->midi.pause(1);
-}
-
-void Debugger_LoK::postEnter() {
- //_vm->midi.pause(0);
-}
-
bool Debugger_LoK::cmd_enterRoom(int argc, const char **argv) {
uint direction = 0;
if (argc > 1) {
@@ -331,7 +333,7 @@ bool Debugger_v2::cmd_enterScene(int argc, const char **argv) {
return false;
}
- DebugPrintf("Syntax: %d <scenenum> <direction>\n", argv[0]);
+ DebugPrintf("Syntax: %s <scenenum> <direction>\n", argv[0]);
return true;
}
diff --git a/engines/kyra/debugger.h b/engines/kyra/debugger.h
index c9cf6dba2a..dfc2a26aa2 100644
--- a/engines/kyra/debugger.h
+++ b/engines/kyra/debugger.h
@@ -41,6 +41,9 @@ public:
virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
KyraEngine_v1 *_vm;
bool cmd_setScreenDebug(int argc, const char **argv);
@@ -62,9 +65,6 @@ public:
protected:
KyraEngine_LoK *_vm;
- virtual void preEnter();
- virtual void postEnter();
-
bool cmd_enterRoom(int argc, const char **argv);
bool cmd_listScenes(int argc, const char **argv);
bool cmd_giveItem(int argc, const char **argv);
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 875b49b62d..6c111a6601 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -50,7 +50,7 @@ const char * const directoryGlobs[] = {
0
};
-const ADParams detectionParams = {
+static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)adGameDescs,
// Size of that superset structure
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 84e0f343a7..5c97df8895 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1146,6 +1146,23 @@ const KYRAGameDescription adGameDescs[] = {
"lol",
"Extracted",
{
+ { "GENERAL.PAK", 0, "d119e3b57f8e5edcbb90980ca6f4215a", -1 },
+ { "CHAPTER7.PAK", 0, "71a3d3cb1554294646a389e5c345cf28", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
{ "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
{ "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
{ 0, 0, 0, 0 }
diff --git a/engines/kyra/gui_hof.cpp b/engines/kyra/gui_hof.cpp
index 621b3199c5..56971e563c 100644
--- a/engines/kyra/gui_hof.cpp
+++ b/engines/kyra/gui_hof.cpp
@@ -122,9 +122,9 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
int inventorySlot = button->index - 6;
- uint16 item = _mainCharacter.inventory[inventorySlot];
- if (_itemInHand == -1) {
- if (item == 0xFFFF)
+ Item item = _mainCharacter.inventory[inventorySlot];
+ if (_itemInHand == kItemNone) {
+ if (item == kItemNone)
return 0;
_screen->hideMouse();
clearInventorySlot(inventorySlot, 0);
@@ -134,9 +134,9 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
updateCommandLineEx(item+54, string, 0xD6);
_itemInHand = (int16)item;
_screen->showMouse();
- _mainCharacter.inventory[inventorySlot] = 0xFFFF;
+ _mainCharacter.inventory[inventorySlot] = kItemNone;
} else {
- if (_mainCharacter.inventory[inventorySlot] != 0xFFFF) {
+ if (_mainCharacter.inventory[inventorySlot] != kItemNone) {
if (checkInventoryItemExchange(_itemInHand, inventorySlot))
return 0;
@@ -160,7 +160,7 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
updateCommandLineEx(_itemInHand+54, string, 0xD6);
_screen->showMouse();
_mainCharacter.inventory[inventorySlot] = _itemInHand;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
}
}
@@ -168,15 +168,15 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
}
int KyraEngine_HoF::scrollInventory(Button *button) {
- uint16 *src = _mainCharacter.inventory;
- uint16 *dst = &_mainCharacter.inventory[10];
- uint16 temp[5];
-
- memcpy(temp, src, sizeof(uint16)*5);
- memcpy(src, src+5, sizeof(uint16)*5);
- memcpy(src+5, dst, sizeof(uint16)*5);
- memcpy(dst, dst+5, sizeof(uint16)*5);
- memcpy(dst+5, temp, sizeof(uint16)*5);
+ Item *src = _mainCharacter.inventory;
+ Item *dst = &_mainCharacter.inventory[10];
+ Item temp[5];
+
+ memcpy(temp, src, sizeof(Item)*5);
+ memcpy(src, src+5, sizeof(Item)*5);
+ memcpy(src+5, dst, sizeof(Item)*5);
+ memcpy(dst, dst+5, sizeof(Item)*5);
+ memcpy(dst+5, temp, sizeof(Item)*5);
_screen->hideMouse();
_screen->copyRegion(0x46, 0x90, 0x46, 0x90, 0x71, 0x2E, 0, 2);
_screen->showMouse();
@@ -185,7 +185,7 @@ int KyraEngine_HoF::scrollInventory(Button *button) {
return 0;
}
-int KyraEngine_HoF::getInventoryItemSlot(uint16 item) {
+int KyraEngine_HoF::getInventoryItemSlot(Item item) {
for (int i = 0; i < 20; ++i) {
if (_mainCharacter.inventory[i] == item)
return i;
@@ -195,14 +195,14 @@ int KyraEngine_HoF::getInventoryItemSlot(uint16 item) {
int KyraEngine_HoF::findFreeVisibleInventorySlot() {
for (int i = 0; i < 10; ++i) {
- if (_mainCharacter.inventory[i] == 0xFFFF)
+ if (_mainCharacter.inventory[i] == kItemNone)
return i;
}
return -1;
}
void KyraEngine_HoF::removeSlotFromInventory(int slot) {
- _mainCharacter.inventory[slot] = 0xFFFF;
+ _mainCharacter.inventory[slot] = kItemNone;
if (slot < 10) {
_screen->hideMouse();
clearInventorySlot(slot, 0);
@@ -210,21 +210,21 @@ void KyraEngine_HoF::removeSlotFromInventory(int slot) {
}
}
-bool KyraEngine_HoF::checkInventoryItemExchange(uint16 handItem, int slot) {
+bool KyraEngine_HoF::checkInventoryItemExchange(Item handItem, int slot) {
bool removeItem = false;
- uint16 newItem = 0xFFFF;
+ Item newItem = kItemNone;
- uint16 invItem = _mainCharacter.inventory[slot];
+ Item invItem = _mainCharacter.inventory[slot];
for (const uint16 *table = _itemMagicTable; *table != 0xFFFF; table += 4) {
- if (table[0] != handItem || table[1] != invItem)
+ if (table[0] != handItem || table[1] != (uint16)invItem)
continue;
if (table[3] == 0xFFFF)
continue;
removeItem = (table[3] == 1);
- newItem = table[2];
+ newItem = (Item)table[2];
snd_playSoundEffect(0x68);
_mainCharacter.inventory[slot] = newItem;
@@ -246,7 +246,7 @@ bool KyraEngine_HoF::checkInventoryItemExchange(uint16 handItem, int slot) {
return false;
}
-void KyraEngine_HoF::drawInventoryShape(int page, uint16 item, int slot) {
+void KyraEngine_HoF::drawInventoryShape(int page, Item item, int slot) {
_screen->drawShape(page, getShapePtr(item+64), _inventoryX[slot], _inventoryY[slot], 0, 0);
_screen->updateScreen();
}
@@ -260,11 +260,11 @@ void KyraEngine_HoF::redrawInventory(int page) {
int pageBackUp = _screen->_curPage;
_screen->_curPage = page;
- const uint16 *inventory = _mainCharacter.inventory;
+ const Item *inventory = _mainCharacter.inventory;
_screen->hideMouse();
for (int i = 0; i < 10; ++i) {
clearInventorySlot(i, page);
- if (inventory[i] != 0xFFFF) {
+ if (inventory[i] != kItemNone) {
_screen->drawShape(page, getShapePtr(inventory[i]+64), _inventoryX[i], _inventoryY[i], 0, 0);
drawInventoryShape(page, inventory[i], i);
}
@@ -734,7 +734,7 @@ int GUI_HoF::optionsButton(Button *button) {
if (_loadedSave) {
if (_restartGame)
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
} else {
restorePage1(_vm->_screenBuffer);
restorePalette();
@@ -820,7 +820,7 @@ void GUI_HoF::resetState(int item) {
_vm->setNextIdleAnimTimer();
_isDeathMenu = false;
if (!_loadedSave) {
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
_vm->setHandItem(item);
} else {
_vm->setHandItem(_vm->_itemInHand);
@@ -998,7 +998,7 @@ int GUI_HoF::gameOptionsTalkie(Button *caller) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(999, "Autosave", &thumb);
+ _vm->saveGameStateIntern(999, "Autosave", &thumb);
thumb.free();
_vm->_lastAutosave = _vm->_system->getMillis();
diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp
index 9a1d750391..b7952eb81e 100644
--- a/engines/kyra/gui_lok.cpp
+++ b/engines/kyra/gui_lok.cpp
@@ -32,6 +32,7 @@
#include "kyra/gui_lok.h"
#include "kyra/timer.h"
#include "kyra/util.h"
+#include "kyra/item.h"
#include "common/config-manager.h"
#include "common/savefile.h"
@@ -49,9 +50,9 @@ void KyraEngine_LoK::initMainButtonList() {
int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
int itemOffset = caller->index - 2;
- uint8 inventoryItem = _currentCharacter->inventoryItems[itemOffset];
- if (_itemInHand == -1) {
- if (inventoryItem == 0xFF) {
+ Item inventoryItem = (int8)_currentCharacter->inventoryItems[itemOffset];
+ if (_itemInHand == kItemNone) {
+ if (inventoryItem == kItemNone) {
snd_playSoundEffect(0x36);
return 0;
} else {
@@ -62,10 +63,10 @@ int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
updateSentenceCommand(_itemList[getItemListIndex(inventoryItem)], _takenList[0], 179);
_itemInHand = inventoryItem;
_screen->showMouse();
- _currentCharacter->inventoryItems[itemOffset] = 0xFF;
+ _currentCharacter->inventoryItems[itemOffset] = kItemNone;
}
} else {
- if (inventoryItem != 0xFF) {
+ if (inventoryItem != kItemNone) {
snd_playSoundEffect(0x35);
_screen->hideMouse();
_screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12);
@@ -87,7 +88,7 @@ int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
updateSentenceCommand(_itemList[getItemListIndex(_itemInHand)], _placedList[0], 179);
_screen->showMouse();
_currentCharacter->inventoryItems[itemOffset] = _itemInHand;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
}
}
_screen->updateScreen();
@@ -104,7 +105,7 @@ int KyraEngine_LoK::buttonAmuletCallback(Button *caller) {
}
if (!queryGameFlag(0x2D))
return 1;
- if (_itemInHand != -1) {
+ if (_itemInHand != kItemNone) {
assert(_putDownFirst);
characterSays(2000, _putDownFirst[0], 0, -2);
return 1;
@@ -777,7 +778,7 @@ int GUI_LoK::saveGame(Button *button) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(_vm->_gameToLoad, _savegameName, &thumb);
+ _vm->saveGameStateIntern(_vm->_gameToLoad, _savegameName, &thumb);
thumb.free();
}
}
diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp
index 2c86073892..07fbf1664d 100644
--- a/engines/kyra/gui_lol.cpp
+++ b/engines/kyra/gui_lol.cpp
@@ -2906,7 +2906,7 @@ int GUI_LoL::clickedSavenameMenu(Button *button) {
int slot = _menuResult == -2 ? getNextSavegameSlot() : _menuResult - 1;
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(slot, _saveDescription, &thumb);
+ _vm->saveGameStateIntern(slot, _saveDescription, &thumb);
thumb.free();
_displayMenu = false;
diff --git a/engines/kyra/gui_mr.cpp b/engines/kyra/gui_mr.cpp
index bcc74f5a07..a04ec49324 100644
--- a/engines/kyra/gui_mr.cpp
+++ b/engines/kyra/gui_mr.cpp
@@ -130,7 +130,7 @@ void KyraEngine_MR::showMessageFromCCode(int string, uint8 c0, int) {
showMessage((const char *)getTableEntry(_cCodeFile, string), c0, 0xF0);
}
-void KyraEngine_MR::updateItemCommand(int item, int str, uint8 c0) {
+void KyraEngine_MR::updateItemCommand(Item item, int str, uint8 c0) {
char buffer[100];
char *src = (char *)getTableEntry(_itemFile, item);
@@ -449,7 +449,7 @@ void KyraEngine_MR::redrawInventory(int page) {
for (int i = 0; i < 10; ++i) {
clearInventorySlot(i, page);
- if (_mainCharacter.inventory[i] != 0xFFFF) {
+ if (_mainCharacter.inventory[i] != kItemNone) {
_screen->drawShape(page, getShapePtr(_mainCharacter.inventory[i]+248), _inventoryX[i], _inventoryY[i] + yOffset, 0, 0);
drawInventorySlot(page, _mainCharacter.inventory[i], i);
}
@@ -472,7 +472,7 @@ void KyraEngine_MR::clearInventorySlot(int slot, int page) {
_screen->drawShape(page, getShapePtr(slot+422), _inventoryX[slot], _inventoryY[slot] + yOffset, 0, 0);
}
-void KyraEngine_MR::drawInventorySlot(int page, int item, int slot) {
+void KyraEngine_MR::drawInventorySlot(int page, Item item, int slot) {
int yOffset = 0;
if (page == 30) {
page = 2;
@@ -488,9 +488,9 @@ int KyraEngine_MR::buttonInventory(Button *button) {
return 0;
const int slot = button->index - 5;
- const int16 slotItem = (int16)_mainCharacter.inventory[slot];
- if (_itemInHand == -1) {
- if (slotItem == -1)
+ const Item slotItem = _mainCharacter.inventory[slot];
+ if (_itemInHand == kItemNone) {
+ if (slotItem == kItemNone)
return 0;
_screen->hideMouse();
@@ -499,7 +499,7 @@ int KyraEngine_MR::buttonInventory(Button *button) {
setMouseCursor(slotItem);
updateItemCommand(slotItem, (_lang == 1) ? getItemCommandStringPickUp(slotItem) : 0, 0xFF);
_itemInHand = slotItem;
- _mainCharacter.inventory[slot] = 0xFFFF;
+ _mainCharacter.inventory[slot] = kItemNone;
_screen->showMouse();
} else if (_itemInHand == 27) {
if (_chatText)
@@ -528,7 +528,7 @@ int KyraEngine_MR::buttonInventory(Button *button) {
updateItemCommand(_itemInHand, (_lang == 1) ? getItemCommandStringInv(_itemInHand) : 2, 0xFF);
_screen->showMouse();
_mainCharacter.inventory[slot] = _itemInHand;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
}
}
@@ -635,7 +635,7 @@ int KyraEngine_MR::buttonJesterStaff(Button *button) {
updateItemCommand(27, 2, 0xFF);
setGameFlag(0x97);
_screen->showMouse();
- } else if (_itemInHand == -1) {
+ } else if (_itemInHand == kItemNone) {
if (queryGameFlag(0x97)) {
_screen->hideMouse();
snd_playSoundEffect(0x0B, 0xC8);
@@ -1141,7 +1141,7 @@ void GUI_MR::resetState(int item) {
_vm->setNextIdleAnimTimer();
_isDeathMenu = false;
if (!_loadedSave) {
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
_vm->setHandItem(item);
} else {
_vm->setHandItem(_vm->_itemInHand);
@@ -1239,7 +1239,7 @@ int GUI_MR::optionsButton(Button *button) {
if (_loadedSave) {
if (_restartGame)
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
} else {
restorePage1(_vm->_screenBuffer);
}
@@ -1380,7 +1380,7 @@ int GUI_MR::gameOptions(Button *caller) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(999, "Autosave", &thumb);
+ _vm->saveGameStateIntern(999, "Autosave", &thumb);
thumb.free();
_vm->_lastAutosave = _vm->_system->getMillis();
diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp
index fe4b54d09b..2247a0ca2e 100644
--- a/engines/kyra/gui_v2.cpp
+++ b/engines/kyra/gui_v2.cpp
@@ -625,7 +625,7 @@ int GUI_v2::saveMenu(Button *caller) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
Util::convertDOSToISO(_saveDescription);
- _vm->saveGameState(_saveSlot, _saveDescription, &thumb);
+ _vm->saveGameStateIntern(_saveSlot, _saveDescription, &thumb);
thumb.free();
_displayMenu = false;
diff --git a/engines/sword25/kernel/bs_stdint.h b/engines/kyra/item.h
index c1970bff3e..2088f4bd8b 100644
--- a/engines/sword25/kernel/bs_stdint.h
+++ b/engines/kyra/item.h
@@ -23,32 +23,23 @@
*
*/
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
+#ifndef KYRA_ITEM_H
+#define KYRA_ITEM_H
-// TODO: Properly replace all game occurances that use these types with proper ScummVM types, and remove this file
+#include "common/scummsys.h"
-#ifndef SWORD25_STDINT_H
-#define SWORD25_STDINT_H
+namespace Kyra {
-#include "common/scummsys.h"
+typedef int16 Item;
-typedef uint8 uint8_t;
-typedef uint16 uint16_t;
-typedef uint32 uint32_t;
-typedef int8 int8_t;
-typedef int16 int16_t;
-typedef int32 int32_t;
+enum {
+ /**
+ * Constant for invalid item.
+ */
+ kItemNone = -1
+};
-typedef unsigned long long uint64_t;
-typedef signed long long int64_t;
-typedef unsigned long long uint64;
-typedef signed long long int64;
+} // End of namespace Kyra
#endif
+
diff --git a/engines/kyra/items_hof.cpp b/engines/kyra/items_hof.cpp
index 876db58716..6a78a77c23 100644
--- a/engines/kyra/items_hof.cpp
+++ b/engines/kyra/items_hof.cpp
@@ -31,9 +31,9 @@ int KyraEngine_HoF::checkItemCollision(int x, int y) {
int itemPos = -1, yPos = -1;
for (int i = 0; i < 30; ++i) {
- const Item &curItem = _itemList[i];
+ const ItemDefinition &curItem = _itemList[i];
- if (curItem.id == 0xFFFF || curItem.sceneId != _mainCharacter.sceneId)
+ if (curItem.id == kItemNone || curItem.sceneId != _mainCharacter.sceneId)
continue;
int itemX1 = curItem.x - 8 - 3;
@@ -79,7 +79,7 @@ void KyraEngine_HoF::updateWaterFlasks() {
}
}
-bool KyraEngine_HoF::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
+bool KyraEngine_HoF::dropItem(int unk1, Item item, int x, int y, int unk2) {
if (_mouseState <= -1)
return false;
@@ -93,7 +93,7 @@ bool KyraEngine_HoF::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
return success;
}
-bool KyraEngine_HoF::processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2) {
+bool KyraEngine_HoF::processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2) {
int itemPos = checkItemCollision(x, y);
if (unk1)
@@ -108,7 +108,7 @@ bool KyraEngine_HoF::processItemDrop(uint16 sceneId, uint16 item, int x, int y,
if (unk1 != 3) {
for (int i = 0; i < 30; ++i) {
- if (_itemList[i].id == 0xFFFF) {
+ if (_itemList[i].id == kItemNone) {
freeItemSlot = i;
break;
}
@@ -200,7 +200,7 @@ bool KyraEngine_HoF::processItemDrop(uint16 sceneId, uint16 item, int x, int y,
return true;
}
-void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item) {
+void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item) {
uint8 *itemShape = getShapePtr(item + 64);
if (startX == dstX && startY == dstY) {
@@ -335,7 +335,7 @@ bool KyraEngine_HoF::pickUpItem(int x, int y) {
_screen->hideMouse();
deleteItemAnimEntry(itemPos);
int itemId = _itemList[itemPos].id;
- _itemList[itemPos].id = 0xFFFF;
+ _itemList[itemPos].id = kItemNone;
snd_playSoundEffect(0x0b);
setMouseCursor(itemId);
int str2 = 7;
@@ -368,8 +368,8 @@ bool KyraEngine_HoF::isDropable(int x, int y) {
return true;
}
-int KyraEngine_HoF::getItemCommandStringDrop(uint16 item) {
- assert(item < _itemStringMapSize);
+int KyraEngine_HoF::getItemCommandStringDrop(Item item) {
+ assert(item >= 0 && item < _itemStringMapSize);
int stringId = _itemStringMap[item];
static const int dropStringIds[] = {
@@ -380,8 +380,8 @@ int KyraEngine_HoF::getItemCommandStringDrop(uint16 item) {
return dropStringIds[stringId];
}
-int KyraEngine_HoF::getItemCommandStringPickUp(uint16 item) {
- assert(item < _itemStringMapSize);
+int KyraEngine_HoF::getItemCommandStringPickUp(Item item) {
+ assert(item >= 0 && item < _itemStringMapSize);
int stringId = _itemStringMap[item];
static const int pickUpStringIds[] = {
@@ -392,8 +392,8 @@ int KyraEngine_HoF::getItemCommandStringPickUp(uint16 item) {
return pickUpStringIds[stringId];
}
-int KyraEngine_HoF::getItemCommandStringInv(uint16 item) {
- assert(item < _itemStringMapSize);
+int KyraEngine_HoF::getItemCommandStringInv(Item item) {
+ assert(item >= 0 && item < _itemStringMapSize);
int stringId = _itemStringMap[item];
static const int pickUpStringIds[] = {
@@ -404,8 +404,8 @@ int KyraEngine_HoF::getItemCommandStringInv(uint16 item) {
return pickUpStringIds[stringId];
}
-bool KyraEngine_HoF::itemIsFlask(int item) {
- for (int i = 0; _flaskTable[i] != -1; ++i) {
+bool KyraEngine_HoF::itemIsFlask(Item item) {
+ for (int i = 0; _flaskTable[i] != kItemNone; ++i) {
if (_flaskTable[i] == item)
return true;
}
@@ -413,12 +413,12 @@ bool KyraEngine_HoF::itemIsFlask(int item) {
return false;
}
-void KyraEngine_HoF::setMouseCursor(uint16 item) {
+void KyraEngine_HoF::setMouseCursor(Item item) {
int shape = 0;
int hotX = 1;
int hotY = 1;
- if (item != 0xFFFF) {
+ if (item != kItemNone) {
hotX = 8;
hotY = 15;
shape = item+64;
diff --git a/engines/kyra/items_lok.cpp b/engines/kyra/items_lok.cpp
index df46dfa4cd..322314e3ad 100644
--- a/engines/kyra/items_lok.cpp
+++ b/engines/kyra/items_lok.cpp
@@ -74,29 +74,31 @@ void KyraEngine_LoK::clearNoDropRects() {
byte KyraEngine_LoK::findFreeItemInScene(int scene) {
assert(scene < _roomTableSize);
Room *room = &_roomTable[scene];
+
for (int i = 0; i < 12; ++i) {
- if (room->itemsTable[i] == 0xFF)
+ if (room->itemsTable[i] == kItemNone)
return i;
}
+
return 0xFF;
}
byte KyraEngine_LoK::findItemAtPos(int x, int y) {
assert(_currentCharacter->sceneId < _roomTableSize);
- const uint8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
+ const int8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
const uint16 *xposOffset = _roomTable[_currentCharacter->sceneId].itemsXPos;
const uint8 *yposOffset = _roomTable[_currentCharacter->sceneId].itemsYPos;
int highestYPos = -1;
- byte returnValue = 0xFF;
+ Item returnValue = kItemNone;
for (int i = 0; i < 12; ++i) {
- if (*itemsTable != 0xFF) {
+ if (*itemsTable != kItemNone) {
int xpos = *xposOffset - 11;
int xpos2 = *xposOffset + 10;
if (x > xpos && x < xpos2) {
- assert(*itemsTable < ARRAYSIZE(_itemTable));
- int itemHeight = _itemTable[*itemsTable].height;
+ assert(*itemsTable >= 0);
+ int itemHeight = _itemHtDat[*itemsTable];
int ypos = *yposOffset + 3;
int ypos2 = ypos - itemHeight - 3;
@@ -108,6 +110,7 @@ byte KyraEngine_LoK::findItemAtPos(int x, int y) {
}
}
}
+
++xposOffset;
++yposOffset;
++itemsTable;
@@ -170,7 +173,7 @@ void KyraEngine_LoK::placeItemInGenericMapScene(int item, int index) {
}
}
-void KyraEngine_LoK::setHandItem(uint16 item) {
+void KyraEngine_LoK::setHandItem(Item item) {
_screen->hideMouse();
setMouseItem(item);
_itemInHand = item;
@@ -180,20 +183,21 @@ void KyraEngine_LoK::setHandItem(uint16 item) {
void KyraEngine_LoK::removeHandItem() {
_screen->hideMouse();
_screen->setMouseCursor(1, 1, _shapes[0]);
- _itemInHand = -1;
+ _itemInHand = kItemNone;
_screen->showMouse();
}
-void KyraEngine_LoK::setMouseItem(uint16 item) {
- if (item == 0xFFFF)
+void KyraEngine_LoK::setMouseItem(Item item) {
+ if (item == kItemNone)
_screen->setMouseCursor(1, 1, _shapes[6]);
else
_screen->setMouseCursor(8, 15, _shapes[216+item]);
}
void KyraEngine_LoK::wipeDownMouseItem(int xpos, int ypos) {
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
return;
+
xpos -= 8;
ypos -= 15;
_screen->hideMouse();
@@ -261,7 +265,7 @@ int KyraEngine_LoK::countItemsInScene(uint16 sceneId) {
int items = 0;
for (int i = 0; i < 12; ++i) {
- if (currentRoom->itemsTable[i] != 0xFF)
+ if (currentRoom->itemsTable[i] != kItemNone)
++items;
}
@@ -284,7 +288,7 @@ int KyraEngine_LoK::processItemDrop(uint16 sceneId, uint8 item, int x, int y, in
if (unk1 != 3) {
for (int i = 0; i < 12; ++i) {
- if (currentRoom->itemsTable[i] == 0xFF) {
+ if (currentRoom->itemsTable[i] == kItemNone) {
freeItem = i;
break;
}
@@ -301,13 +305,14 @@ int KyraEngine_LoK::processItemDrop(uint16 sceneId, uint8 item, int x, int y, in
return 1;
}
- int itemHeight = _itemTable[item].height;
+ int itemHeight = _itemHtDat[item];
_lastProcessedItemHeight = itemHeight;
- if (x == -1 && x == -1) {
+ if (x == -1)
x = _rnd.getRandomNumberRng(16, 304);
+
+ if (y == -1)
y = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 135);
- }
int xpos = x;
int ypos = y;
@@ -618,7 +623,7 @@ void KyraEngine_LoK::itemSpecialFX1(int x, int y, int item) {
void KyraEngine_LoK::itemSpecialFX2(int x, int y, int item) {
x -= 8;
y -= 15;
- int yAdd = (int8)(((16 - _itemTable[item].height) >> 1) & 0xFF);
+ int yAdd = (int8)(((16 - _itemHtDat[item]) >> 1) & 0xFF);
backUpItemRect0(x, y);
if (item >= 80 && item <= 89)
snd_playSoundEffect(55);
@@ -646,7 +651,8 @@ void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
int videoPageBackUp = _screen->_curPage;
_screen->_curPage = 0;
int x = 0, y = 0;
- if (itemPos == -1) {
+
+ if (itemPos == kItemNone) {
Common::Point mouse = getMousePos();
x = mouse.x - 12;
y = mouse.y - 18;
@@ -655,7 +661,7 @@ void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
y = _itemPosY[itemPos] - 3;
}
- if (_itemInHand == -1 && itemPos == -1)
+ if (_itemInHand == kItemNone && itemPos == -1)
return;
int tableIndex = 0, loopStart = 0, maxLoops = 0;
@@ -712,15 +718,17 @@ void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
delayUntil(nextTime);
}
restoreItemRect1(x, y);
+
if (itemPos == -1) {
_screen->setMouseCursor(1, 1, _shapes[0]);
- _itemInHand = -1;
+ _itemInHand = kItemNone;
} else {
- _characterList[0].inventoryItems[itemPos] = 0xFF;
+ _characterList[0].inventoryItems[itemPos] = kItemNone;
_screen->hideMouse();
_screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, 0);
_screen->showMouse();
}
+
_screen->showMouse();
_screen->_curPage = videoPageBackUp;
}
@@ -883,7 +891,8 @@ void KyraEngine_LoK::redrawInventory(int page) {
_screen->hideMouse();
for (int i = 0; i < 10; ++i) {
_screen->fillRect(_itemPosX[i], _itemPosY[i], _itemPosX[i] + 15, _itemPosY[i] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, page);
- if (_currentCharacter->inventoryItems[i] != 0xFF) {
+
+ if (_currentCharacter->inventoryItems[i] != kItemNone) {
uint8 item = _currentCharacter->inventoryItems[i];
_screen->drawShape(page, _shapes[216+item], _itemPosX[i], _itemPosY[i], 0, 0);
}
@@ -913,12 +922,12 @@ void KyraEngine_LoK::restoreItemRect1(int xpos, int ypos) {
_screen->copyBlockToPage(_screen->_curPage, xpos, ypos, 4<<3, 32, _itemBkgBackUp[1]);
}
-int KyraEngine_LoK::getItemListIndex(uint16 item) {
+int KyraEngine_LoK::getItemListIndex(Item item) {
if (_flags.platform != Common::kPlatformAmiga)
return item;
// "Unknown item" is at 81.
- if (item == 0xFFFF || item == 0xFF)
+ if (item == kItemNone)
return 81;
// The first item names are mapped directly
else if (item <= 28)
diff --git a/engines/kyra/items_lol.cpp b/engines/kyra/items_lol.cpp
index 5b566d51db..d4567ad737 100644
--- a/engines/kyra/items_lol.cpp
+++ b/engines/kyra/items_lol.cpp
@@ -110,10 +110,10 @@ void LoLEngine::takeCredits(int credits, int redraw) {
}
}
-int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
+Item LoLEngine::makeItem(int itemType, int curFrame, int flags) {
int cnt = 0;
int r = 0;
- int i = 1;
+ Item i = 1;
for (; i < 400; i++) {
if (_itemsInPlay[i].shpCurFrame_flg & 0x8000) {
@@ -130,7 +130,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
continue;
bool t = false;
- int ii = i;
+ Item ii = i;
while (ii && !t) {
t = testUnkItemFlags(ii);
if (t)
@@ -145,7 +145,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
}
}
- int slot = i;
+ Item slot = i;
if (cnt) {
slot = r;
if (testUnkItemFlags(r)) {
@@ -154,7 +154,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
deleteItem(r);
slot = r;
} else {
- int ii = _itemsInPlay[slot].nextAssignedObject;
+ uint16 ii = _itemsInPlay[slot].nextAssignedObject;
while (ii) {
if (testUnkItemFlags(ii)) {
_itemsInPlay[slot].nextAssignedObject = _itemsInPlay[ii].nextAssignedObject;
@@ -178,7 +178,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
return slot;
}
-void LoLEngine::placeMoveLevelItem(int itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight) {
+void LoLEngine::placeMoveLevelItem(Item itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight) {
calcCoordinates(_itemsInPlay[itemIndex].x, _itemsInPlay[itemIndex].y, block, xOffs, yOffs);
if (_itemsInPlay[itemIndex].block)
@@ -194,7 +194,7 @@ void LoLEngine::placeMoveLevelItem(int itemIndex, int level, int block, int xOff
}
}
-bool LoLEngine::addItemToInventory(int itemIndex) {
+bool LoLEngine::addItemToInventory(Item itemIndex) {
int pos = 0;
int i = 0;
@@ -222,7 +222,7 @@ bool LoLEngine::addItemToInventory(int itemIndex) {
return true;
}
-bool LoLEngine::testUnkItemFlags(int itemIndex) {
+bool LoLEngine::testUnkItemFlags(Item itemIndex) {
if (!(_itemsInPlay[itemIndex].shpCurFrame_flg & 0x4000))
return false;
@@ -233,7 +233,7 @@ bool LoLEngine::testUnkItemFlags(int itemIndex) {
}
-void LoLEngine::deleteItem(int itemIndex) {
+void LoLEngine::deleteItem(Item itemIndex) {
memset(&_itemsInPlay[itemIndex], 0, sizeof(ItemInPlay));
_itemsInPlay[itemIndex].shpCurFrame_flg |= 0x8000;
}
@@ -245,7 +245,7 @@ ItemInPlay *LoLEngine::findObject(uint16 index) {
return &_itemsInPlay[index];
}
-void LoLEngine::runItemScript(int charNum, int item, int flags, int next, int reg4) {
+void LoLEngine::runItemScript(int charNum, Item item, int flags, int next, int reg4) {
EMCState scriptState;
memset(&scriptState, 0, sizeof(EMCState));
@@ -270,7 +270,7 @@ void LoLEngine::runItemScript(int charNum, int item, int flags, int next, int re
}
}
-void LoLEngine::setHandItem(uint16 itemIndex) {
+void LoLEngine::setHandItem(Item itemIndex) {
if (itemIndex && _itemProperties[_itemsInPlay[itemIndex].itemPropertyIndex].flags & 0x80) {
runItemScript(-1, itemIndex, 0x400, 0, 0);
if (_itemsInPlay[itemIndex].shpCurFrame_flg & 0x8000)
@@ -307,7 +307,7 @@ bool LoLEngine::itemEquipped(int charNum, uint16 itemType) {
return false;
}
-void LoLEngine::setItemPosition(int item, uint16 x, uint16 y, int flyingHeight, int b) {
+void LoLEngine::setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, int b) {
if (!flyingHeight) {
x = (x & 0xffc0) | 0x40;
y = (y & 0xffc0) | 0x40;
@@ -334,7 +334,7 @@ void LoLEngine::setItemPosition(int item, uint16 x, uint16 y, int flyingHeight,
checkSceneUpdateNeed(block);
}
-void LoLEngine::removeLevelItem(int item, int block) {
+void LoLEngine::removeLevelItem(Item item, int block) {
removeAssignedObjectFromBlock(&_levelBlockProperties[block], item);
removeDrawObjectFromBlock(&_levelBlockProperties[block], item);
runLevelScriptCustom(block, 0x100, -1, item, 0, 0);
@@ -342,7 +342,7 @@ void LoLEngine::removeLevelItem(int item, int block) {
_itemsInPlay[item].level = 0;
}
-bool LoLEngine::launchObject(int objectType, int item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c) {
+bool LoLEngine::launchObject(int objectType, Item item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c) {
int sp = checkDrawObjectSpace(_partyPosX, _partyPosY, startX, startY);
FlyingObject *t = _flyingObjects;
int slot = -1;
@@ -521,10 +521,10 @@ int LoLEngine::checkDrawObjectSpace(int itemX, int itemY, int partyX, int partyY
return a + b;
}
-int LoLEngine::checkSceneForItems(uint16 *blockDrawObjects, int colour) {
+int LoLEngine::checkSceneForItems(uint16 *blockDrawObjects, int color) {
while (*blockDrawObjects) {
if (!(*blockDrawObjects & 0x8000)) {
- if (!--colour)
+ if (!--color)
return *blockDrawObjects;
}
diff --git a/engines/kyra/items_mr.cpp b/engines/kyra/items_mr.cpp
index 256753cc69..2bc268ace3 100644
--- a/engines/kyra/items_mr.cpp
+++ b/engines/kyra/items_mr.cpp
@@ -29,7 +29,7 @@
namespace Kyra {
void KyraEngine_MR::removeTrashItems() {
- for (int i = 0; _trashItemList[i] != 0xFF; ++i) {
+ for (int i = 0; _trashItemList[i] != kItemNone; ++i) {
for (int item = findItem(_trashItemList[i]); item != -1; item = findItem(_trashItemList[i])) {
if (_itemList[item].sceneId != _mainCharacter.sceneId)
resetItem(item);
@@ -41,7 +41,7 @@ void KyraEngine_MR::removeTrashItems() {
int KyraEngine_MR::findFreeInventorySlot() {
for (int i = 0; i < 10; ++i) {
- if (_mainCharacter.inventory[i] == 0xFFFF)
+ if (_mainCharacter.inventory[i] == kItemNone)
return i;
}
return -1;
@@ -52,7 +52,7 @@ int KyraEngine_MR::checkItemCollision(int x, int y) {
int maxItemY = -1;
for (int i = 0; i < 50; ++i) {
- if (_itemList[i].id == 0xFFFF || _itemList[i].sceneId != _mainCharacter.sceneId)
+ if (_itemList[i].id == kItemNone || _itemList[i].sceneId != _mainCharacter.sceneId)
continue;
const int x1 = _itemList[i].x - 11;
@@ -76,12 +76,12 @@ int KyraEngine_MR::checkItemCollision(int x, int y) {
return itemIndex;
}
-void KyraEngine_MR::setMouseCursor(uint16 item) {
+void KyraEngine_MR::setMouseCursor(Item item) {
int shape = 0;
int hotX = 1;
int hotY = 1;
- if (item != 0xFFFF) {
+ if (item != kItemNone) {
hotX = 12;
hotY = 19;
shape = item+248;
@@ -94,13 +94,13 @@ void KyraEngine_MR::setMouseCursor(uint16 item) {
void KyraEngine_MR::setItemMouseCursor() {
_mouseState = _itemInHand;
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
_screen->setMouseCursor(0, 0, _gameShapes[0]);
else
_screen->setMouseCursor(12, 19, _gameShapes[_itemInHand+248]);
}
-bool KyraEngine_MR::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
+bool KyraEngine_MR::dropItem(int unk1, Item item, int x, int y, int unk2) {
if (_mouseState <= -1)
return false;
@@ -123,7 +123,7 @@ bool KyraEngine_MR::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
return false;
}
-bool KyraEngine_MR::processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2) {
+bool KyraEngine_MR::processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2) {
int itemPos = checkItemCollision(x, y);
if (unk1)
@@ -138,7 +138,7 @@ bool KyraEngine_MR::processItemDrop(uint16 sceneId, uint16 item, int x, int y, i
if (unk2 != 3) {
for (int i = 0; i < 50; ++i) {
- if (_itemList[i].id == 0xFFFF) {
+ if (_itemList[i].id == kItemNone) {
freeItemSlot = i;
break;
}
@@ -227,7 +227,7 @@ bool KyraEngine_MR::processItemDrop(uint16 sceneId, uint16 item, int x, int y, i
return true;
}
-void KyraEngine_MR::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item, int remove) {
+void KyraEngine_MR::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item, int remove) {
if (startX == dstX && startY == dstY) {
_itemList[itemSlot].x = dstX;
_itemList[itemSlot].y = dstY;
@@ -323,7 +323,7 @@ void KyraEngine_MR::exchangeMouseItem(int itemPos, int runScript) {
_screen->hideMouse();
deleteItemAnimEntry(itemPos);
- int itemId = _itemList[itemPos].id;
+ Item itemId = _itemList[itemPos].id;
_itemList[itemPos].id = _itemInHand;
_itemInHand = itemId;
@@ -353,8 +353,8 @@ bool KyraEngine_MR::pickUpItem(int x, int y, int runScript) {
} else {
_screen->hideMouse();
deleteItemAnimEntry(itemPos);
- int itemId = _itemList[itemPos].id;
- _itemList[itemPos].id = 0xFFFF;
+ Item itemId = _itemList[itemPos].id;
+ _itemList[itemPos].id = kItemNone;
snd_playSoundEffect(0x0B, 0xC8);
setMouseCursor(itemId);
int itemString = 0;
@@ -387,8 +387,9 @@ bool KyraEngine_MR::isDropable(int x, int y) {
return true;
}
-bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
- uint16 item = _itemList[itemSlot].id;
+bool KyraEngine_MR::itemListMagic(Item handItem, int itemSlot) {
+ Item item = _itemList[itemSlot].id;
+
if (_currentChapter == 1 && handItem == 3 && item == 3 && queryGameFlag(0x76)) {
eelScript();
return true;
@@ -410,7 +411,7 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
}
deleteItemAnimEntry(itemSlot);
- _itemList[itemSlot].id = 0xFFFF;
+ _itemList[itemSlot].id = kItemNone;
_screen->showMouse();
return true;
}
@@ -430,7 +431,7 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
}
for (int i = 0; _itemMagicTable[i] != 0xFF; i += 4) {
- if (_itemMagicTable[i+0] != handItem || _itemMagicTable[i+1] != item)
+ if (_itemMagicTable[i+0] != handItem || (int8)_itemMagicTable[i+1] != item)
continue;
uint8 resItem = _itemMagicTable[i+2];
@@ -438,7 +439,7 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
snd_playSoundEffect(0x0F, 0xC8);
- _itemList[itemSlot].id = (resItem == 0xFF) ? 0xFFFF : resItem;
+ _itemList[itemSlot].id = (int8)resItem;
_screen->hideMouse();
deleteItemAnimEntry(itemSlot);
@@ -465,8 +466,9 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
return false;
}
-bool KyraEngine_MR::itemInventoryMagic(int handItem, int invSlot) {
- uint16 item = _mainCharacter.inventory[invSlot];
+bool KyraEngine_MR::itemInventoryMagic(Item handItem, int invSlot) {
+ Item item = _mainCharacter.inventory[invSlot];
+
if (_currentChapter == 1 && handItem == 3 && item == 3 && queryGameFlag(0x76)) {
eelScript();
return true;
@@ -482,7 +484,7 @@ bool KyraEngine_MR::itemInventoryMagic(int handItem, int invSlot) {
delay(1*_tickLength, true);
}
- _mainCharacter.inventory[invSlot] = 0xFFFF;
+ _mainCharacter.inventory[invSlot] = kItemNone;
clearInventorySlot(invSlot, 0);
_screen->showMouse();
return true;
@@ -497,7 +499,7 @@ bool KyraEngine_MR::itemInventoryMagic(int handItem, int invSlot) {
snd_playSoundEffect(0x0F, 0xC8);
- _mainCharacter.inventory[invSlot] = (resItem == 0xFF) ? 0xFFFF : resItem;
+ _mainCharacter.inventory[invSlot] = (int8)resItem;
_screen->hideMouse();
clearInventorySlot(invSlot, 0);
diff --git a/engines/kyra/items_v2.cpp b/engines/kyra/items_v2.cpp
index 29901b2ddb..90b6194f0d 100644
--- a/engines/kyra/items_v2.cpp
+++ b/engines/kyra/items_v2.cpp
@@ -31,9 +31,9 @@ namespace Kyra {
void KyraEngine_v2::initItemList(int size) {
delete[] _itemList;
- _itemList = new Item[size];
+ _itemList = new ItemDefinition[size];
assert(_itemList);
- memset(_itemList, 0, sizeof(Item)*size);
+ memset(_itemList, 0, sizeof(ItemDefinition)*size);
_itemListSize = size;
resetItemList();
@@ -41,7 +41,7 @@ void KyraEngine_v2::initItemList(int size) {
int KyraEngine_v2::findFreeItem() {
for (int i = 0; i < _itemListSize; ++i) {
- if (_itemList[i].id == 0xFFFF)
+ if (_itemList[i].id == kItemNone)
return i;
}
return -1;
@@ -50,13 +50,13 @@ int KyraEngine_v2::findFreeItem() {
int KyraEngine_v2::countAllItems() {
int num = 0;
for (int i = 0; i < _itemListSize; ++i) {
- if (_itemList[i].id != 0xFFFF)
+ if (_itemList[i].id != kItemNone)
++num;
}
return num;
}
-int KyraEngine_v2::findItem(uint16 sceneId, uint16 id) {
+int KyraEngine_v2::findItem(uint16 sceneId, Item id) {
for (int i = 0; i < _itemListSize; ++i) {
if (_itemList[i].id == id && _itemList[i].sceneId == sceneId)
return i;
@@ -64,7 +64,7 @@ int KyraEngine_v2::findItem(uint16 sceneId, uint16 id) {
return -1;
}
-int KyraEngine_v2::findItem(uint16 item) {
+int KyraEngine_v2::findItem(Item item) {
for (int i = 0; i < _itemListSize; ++i) {
if (_itemList[i].id == item)
return i;
@@ -78,17 +78,17 @@ void KyraEngine_v2::resetItemList() {
}
void KyraEngine_v2::resetItem(int index) {
- _itemList[index].id = 0xFFFF;
+ _itemList[index].id = kItemNone;
_itemList[index].sceneId = 0xFFFF;
_itemList[index].x = 0;
_itemList[index].y = 0;
}
-void KyraEngine_v2::setHandItem(uint16 item) {
+void KyraEngine_v2::setHandItem(Item item) {
Screen *scr = screen();
scr->hideMouse();
- if (item == 0xFFFF) {
+ if (item == kItemNone) {
removeHandItem();
} else {
setMouseCursor(item);
@@ -102,8 +102,8 @@ void KyraEngine_v2::removeHandItem() {
Screen *scr = screen();
scr->hideMouse();
scr->setMouseCursor(0, 0, getShapePtr(0));
- _itemInHand = -1;
- _mouseState = -1;
+ _itemInHand = kItemNone;
+ _mouseState = kItemNone;
scr->showMouse();
}
diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp
index 0fafaa15ce..5c471a8c8b 100644
--- a/engines/kyra/kyra_hof.cpp
+++ b/engines/kyra/kyra_hof.cpp
@@ -74,7 +74,7 @@ KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEn
_mainCharX = _mainCharY = -1;
_drawNoShapeFlag = false;
_charPalEntry = 0;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
_unkSceneScreenFlag1 = false;
_noScriptEnter = true;
_currentChapter = 0;
@@ -441,7 +441,7 @@ void KyraEngine_HoF::startup() {
if (_gameToLoad == -1) {
snd_playWanderScoreViaMap(52, 1);
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
- saveGameState(0, "New Game", 0);
+ saveGameStateIntern(0, "New Game", 0);
} else {
loadGameStateCheck(_gameToLoad);
}
@@ -507,7 +507,7 @@ void KyraEngine_HoF::runLoop() {
update();
if (inputFlag == 198 || inputFlag == 199) {
- _unk3 = _mouseState;
+ _savedMouseState = _mouseState;
handleInput(_mouseX, _mouseY);
}
@@ -528,7 +528,7 @@ void KyraEngine_HoF::handleInput(int x, int y) {
if (!_screen->isMouseVisible())
return;
- if (_unk3 == -2) {
+ if (_savedMouseState == -2) {
snd_playSoundEffect(13);
return;
}
@@ -537,8 +537,8 @@ void KyraEngine_HoF::handleInput(int x, int y) {
if (x <= 6 || x >= 312 || y <= 6 || y >= 135) {
bool exitOk = false;
- assert(_unk3 + 6 >= 0);
- switch (_unk3 + 6) {
+ assert(_savedMouseState + 6 >= 0);
+ switch (_savedMouseState + 6) {
case 0:
if (_sceneExit1 != 0xFFFF)
exitOk = true;
@@ -569,7 +569,7 @@ void KyraEngine_HoF::handleInput(int x, int y) {
}
}
- if (checkCharCollision(x, y) && _unk3 >= -1) {
+ if (checkCharCollision(x, y) && _savedMouseState >= -1) {
runSceneScript2();
return;
} else if (pickUpItem(x, y)) {
@@ -609,7 +609,7 @@ void KyraEngine_HoF::handleInput(int x, int y) {
dropItem(0, _itemInHand, x, y, 1);
} else {
- if (_unk3 == -2 || y > 135)
+ if (_savedMouseState == -2 || y > 135)
return;
if (!_unk5) {
@@ -790,7 +790,7 @@ void KyraEngine_HoF::updateMouse() {
if ((mouse.y > 145) || (mouse.x > 6 && mouse.x < 312 && mouse.y > 6 && mouse.y < 135)) {
_mouseState = _itemInHand;
_screen->hideMouse();
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
_screen->setMouseCursor(0, 0, getShapePtr(0));
else
_screen->setMouseCursor(8, 15, getShapePtr(_itemInHand+64));
@@ -1169,25 +1169,25 @@ int KyraEngine_HoF::inputSceneChange(int x, int y, int unk1, int unk2) {
_pathfinderFlag = 15;
if (!_unkHandleSceneChangeFlag) {
- if (_unk3 == -3) {
+ if (_savedMouseState == -3) {
if (_sceneList[curScene].exit4 != 0xFFFF) {
x = 4;
y = _sceneEnterY4;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -5) {
+ } else if (_savedMouseState == -5) {
if (_sceneList[curScene].exit2 != 0xFFFF) {
x = 316;
y = _sceneEnterY2;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -6) {
+ } else if (_savedMouseState == -6) {
if (_sceneList[curScene].exit1 != 0xFFFF) {
x = _sceneEnterX1;
y = _sceneEnterY1 - 2;
_pathfinderFlag = 14;
}
- } else if (_unk3 == -4) {
+ } else if (_savedMouseState == -4) {
if (_sceneList[curScene].exit3 != 0xFFFF) {
x = _sceneEnterX3;
y = 147;
@@ -1200,13 +1200,13 @@ int KyraEngine_HoF::inputSceneChange(int x, int y, int unk1, int unk2) {
int vocH = _flags.isTalkie ? 131 : -1;
if (_pathfinderFlag) {
- if (findItem(curScene, 13) >= 0 && _unk3 <= -3) {
+ if (findItem(curScene, 13) >= 0 && _savedMouseState <= -3) {
strId = 252;
} else if (_itemInHand == 72) {
strId = 257;
- } else if (findItem(curScene, 72) >= 0 && _unk3 <= -3) {
+ } else if (findItem(curScene, 72) >= 0 && _savedMouseState <= -3) {
strId = 256;
- } else if (getInventoryItemSlot(72) != -1 && _unk3 <= -3) {
+ } else if (getInventoryItemSlot(72) != -1 && _savedMouseState <= -3) {
strId = 257;
}
}
diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h
index 05d04fe993..576232740b 100644
--- a/engines/kyra/kyra_hof.h
+++ b/engines/kyra/kyra_hof.h
@@ -443,16 +443,16 @@ protected:
bool lineIsPassable(int x, int y);
// item
- void setMouseCursor(uint16 item);
+ void setMouseCursor(Item item);
uint8 _itemHtDat[176];
int checkItemCollision(int x, int y);
void updateWaterFlasks();
- bool dropItem(int unk1, uint16 item, int x, int y, int unk2);
- bool processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2);
- void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item);
+ bool dropItem(int unk1, Item item, int x, int y, int unk2);
+ bool processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2);
+ void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item);
void exchangeMouseItem(int itemPos);
bool pickUpItem(int x, int y);
@@ -461,18 +461,18 @@ protected:
static const byte _itemStringMap[];
static const int _itemStringMapSize;
- static const int16 _flaskTable[];
- bool itemIsFlask(int item);
+ static const Item _flaskTable[];
+ bool itemIsFlask(Item item);
// inventory
static const int _inventoryX[];
static const int _inventoryY[];
static const uint16 _itemMagicTable[];
- int getInventoryItemSlot(uint16 item);
+ int getInventoryItemSlot(Item item);
void removeSlotFromInventory(int slot);
- bool checkInventoryItemExchange(uint16 item, int slot);
- void drawInventoryShape(int page, uint16 item, int slot);
+ bool checkInventoryItemExchange(Item item, int slot);
+ void drawInventoryShape(int page, Item item, int slot);
void clearInventorySlot(int slot, int page);
void redrawInventory(int page);
void scrollInventoryWheel();
@@ -561,9 +561,9 @@ protected:
void changeFileExtension(char *buffer);
// - Just used in French version
- int getItemCommandStringDrop(uint16 item);
- int getItemCommandStringPickUp(uint16 item);
- int getItemCommandStringInv(uint16 item);
+ int getItemCommandStringDrop(Item item);
+ int getItemCommandStringPickUp(Item item);
+ int getItemCommandStringInv(Item item);
// -
char _internStringBuf[200];
@@ -915,7 +915,7 @@ protected:
int _dbgPass;
// save/load specific
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Common::Error loadGameState(int slot);
};
diff --git a/engines/kyra/kyra_lok.cpp b/engines/kyra/kyra_lok.cpp
index 159230e928..d46fc2d502 100644
--- a/engines/kyra/kyra_lok.cpp
+++ b/engines/kyra/kyra_lok.cpp
@@ -247,7 +247,7 @@ Common::Error KyraEngine_LoK::init() {
_brandonPosX = _brandonPosY = -1;
_poisonDeathCounter = 0;
- memset(_itemTable, 0, sizeof(_itemTable));
+ memset(_itemHtDat, 0, sizeof(_itemHtDat));
memset(_exitList, 0xFFFF, sizeof(_exitList));
_exitListPtr = 0;
_pathfinderFlag = _pathfinderFlag2 = 0;
@@ -260,7 +260,7 @@ Common::Error KyraEngine_LoK::init() {
_marbleVaseItem = -1;
memset(_foyerItemTable, -1, sizeof(_foyerItemTable));
- _itemInHand = -1;
+ _itemInHand = kItemNone;
_currentRoom = 0xFFFF;
_scenePhasingFlag = 0;
@@ -373,7 +373,7 @@ void KyraEngine_LoK::startup() {
for (int i = 0; i < _roomTableSize; ++i) {
for (int item = 0; item < 12; ++item) {
- _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsTable[item] = kItemNone;
_roomTable[i].itemsXPos[item] = 0xFFFF;
_roomTable[i].itemsYPos[item] = 0xFF;
_roomTable[i].needInit[item] = 0;
@@ -420,7 +420,7 @@ void KyraEngine_LoK::startup() {
_gui->buttonMenuCallback(0);
_menuDirectlyToLoad = false;
} else if (!shouldQuit()) {
- saveGameState(0, "New game", 0);
+ saveGameStateIntern(0, "New game", 0);
}
} else {
_screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
@@ -672,12 +672,13 @@ void KyraEngine_LoK::processInput(int xpos, int ypos) {
runNpcScript(script);
return;
}
- if (_itemInHand != -1) {
+ if (_itemInHand != kItemNone) {
if (ypos < 155) {
if (hasClickedOnExit(xpos, ypos)) {
handleSceneChange(xpos, ypos, 1, 1);
return;
}
+
dropItem(0, _itemInHand, xpos, ypos, 1);
}
} else {
@@ -691,14 +692,14 @@ void KyraEngine_LoK::processInput(int xpos, int ypos) {
int KyraEngine_LoK::processInputHelper(int xpos, int ypos) {
uint8 item = findItemAtPos(xpos, ypos);
if (item != 0xFF) {
- if (_itemInHand == -1) {
+ if (_itemInHand == kItemNone) {
_screen->hideMouse();
_animator->animRemoveGameItem(item);
snd_playSoundEffect(53);
assert(_currentCharacter->sceneId < _roomTableSize);
Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
int item2 = currentRoom->itemsTable[item];
- currentRoom->itemsTable[item] = 0xFF;
+ currentRoom->itemsTable[item] = kItemNone;
setMouseItem(item2);
assert(_itemList && _takenList);
updateSentenceCommand(_itemList[getItemListIndex(item2)], _takenList[0], 179);
@@ -830,7 +831,7 @@ void KyraEngine_LoK::updateMousePointer(bool forceUpdate) {
if (mouse.y > 158 || (mouse.x >= 12 && mouse.x < 308 && mouse.y < 136 && mouse.y >= 12) || forceUpdate) {
_mouseState = _itemInHand;
_screen->hideMouse();
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
_screen->setMouseCursor(1, 1, _shapes[0]);
else
_screen->setMouseCursor(8, 15, _shapes[216+_itemInHand]);
diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h
index 50f36d7b71..dfbf5bddd8 100644
--- a/engines/kyra/kyra_lok.h
+++ b/engines/kyra/kyra_lok.h
@@ -30,6 +30,7 @@
#include "kyra/script.h"
#include "kyra/screen_lok.h"
#include "kyra/gui_lok.h"
+#include "kyra/item.h"
namespace Kyra {
@@ -46,7 +47,7 @@ struct Character {
uint8 height;
uint8 facing;
uint16 currentAnimFrame;
- uint8 inventoryItems[10];
+ int8 inventoryItems[10];
int16 x1, y1, x2, y2;
};
@@ -62,19 +63,12 @@ struct Room {
uint16 eastExit;
uint16 southExit;
uint16 westExit;
- uint8 itemsTable[12];
+ int8 itemsTable[12];
uint16 itemsXPos[12];
uint8 itemsYPos[12];
uint8 needInit[12];
};
-struct Item {
- uint8 unk1;
- uint8 height;
- uint8 unk2;
- uint8 unk3;
-};
-
struct SeqLoop {
const uint8 *ptr;
uint16 count;
@@ -218,7 +212,7 @@ public:
protected:
int32 _speechPlayTime;
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Common::Error loadGameState(int slot);
protected:
// input
@@ -288,11 +282,11 @@ protected:
void placeItemInGenericMapScene(int item, int index);
// -> mouse item
- void setHandItem(uint16 item);
+ void setHandItem(Item item);
void removeHandItem();
- void setMouseItem(uint16 item);
+ void setMouseItem(Item item);
- int getItemListIndex(uint16 item);
+ int getItemListIndex(Item item);
// -> graphics effects
void wipeDownMouseItem(int xpos, int ypos);
@@ -402,7 +396,7 @@ protected:
bool _menuDirectlyToLoad;
uint8 *_itemBkgBackUp[2];
uint8 *_shapes[373];
- int8 _itemInHand;
+ Item _itemInHand;
bool _changedScene;
int _unkScreenVar1, _unkScreenVar2, _unkScreenVar3;
int _beadStateVar;
@@ -455,7 +449,7 @@ protected:
int8 *_sceneAnimTable[50];
- Item _itemTable[145];
+ uint8 _itemHtDat[145];
int _lastProcessedItem;
int _lastProcessedItemHeight;
diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp
index c224a76385..5ac6da9f2b 100644
--- a/engines/kyra/kyra_mr.cpp
+++ b/engines/kyra/kyra_mr.cpp
@@ -99,8 +99,8 @@ KyraEngine_MR::KyraEngine_MR(OSystem *system, const GameFlags &flags) : KyraEngi
_unk5 = 0;
_unkSceneScreenFlag1 = false;
_noScriptEnter = true;
- _itemInHand = _mouseState = -1;
- _unk3 = -1;
+ _itemInHand = _mouseState = kItemNone;
+ _savedMouseState = -1;
_unk4 = 0;
_loadingState = false;
_noStartupChat = false;
@@ -653,7 +653,7 @@ void KyraEngine_MR::startup() {
assert(_invWsa);
_invWsa->open("MOODOMTR.WSA", 1, 0);
_invWsaFrame = 6;
- saveGameState(0, "New Game", 0);
+ saveGameStateIntern(0, "New Game", 0);
if (_gameToLoad == -1)
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
else
@@ -966,7 +966,7 @@ void KyraEngine_MR::runLoop() {
_timer->update();
if (inputFlag == 198 || inputFlag == 199) {
- _unk3 = _mouseState;
+ _savedMouseState = _mouseState;
Common::Point mouse = getMousePos();
handleInput(mouse.x, mouse.y);
}
@@ -988,7 +988,7 @@ void KyraEngine_MR::handleInput(int x, int y) {
if (!_screen->isMouseVisible())
return;
- if (_unk3 == -3) {
+ if (_savedMouseState == -3) {
snd_playSoundEffect(0x0D, 0x80);
return;
}
@@ -997,7 +997,7 @@ void KyraEngine_MR::handleInput(int x, int y) {
int skip = 0;
- if (checkCharCollision(x, y) && _unk3 >= -1 && runSceneScript2()) {
+ if (checkCharCollision(x, y) && _savedMouseState >= -1 && runSceneScript2()) {
return;
} else if (_itemInHand != 27 && pickUpItem(x, y, 1)) {
return;
@@ -1023,7 +1023,7 @@ void KyraEngine_MR::handleInput(int x, int y) {
if (checkCharCollision(x, y)) {
if (runSceneScript2())
return;
- } else if (_itemInHand >= 0 && _unk3 >= 0) {
+ } else if (_itemInHand >= 0 && _savedMouseState >= 0) {
if (_itemInHand == 27) {
makeCharFacingMouse();
} else if (y <= 187) {
@@ -1033,10 +1033,10 @@ void KyraEngine_MR::handleInput(int x, int y) {
dropItem(0, _itemInHand, x, y, 1);
}
return;
- } else if (_unk3 == -3) {
+ } else if (_savedMouseState == -3) {
return;
} else {
- if (y > 187 && _unk3 > -4)
+ if (y > 187 && _savedMouseState > -4)
return;
if (_unk5) {
_unk5 = 0;
@@ -1052,25 +1052,25 @@ int KyraEngine_MR::inputSceneChange(int x, int y, int unk1, int unk2) {
_pathfinderFlag = 15;
if (!_unkHandleSceneChangeFlag) {
- if (_unk3 == -4) {
+ if (_savedMouseState == -4) {
if (_sceneList[curScene].exit4 != 0xFFFF) {
x = 4;
y = _sceneEnterY4;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -6) {
+ } else if (_savedMouseState == -6) {
if (_sceneList[curScene].exit2 != 0xFFFF) {
x = 316;
y = _sceneEnterY2;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -7) {
+ } else if (_savedMouseState == -7) {
if (_sceneList[curScene].exit1 != 0xFFFF) {
x = _sceneEnterX1;
y = _sceneEnterY1 - 2;
_pathfinderFlag = 14;
}
- } else if (_unk3 == -5) {
+ } else if (_savedMouseState == -5) {
if (_sceneList[curScene].exit3 != 0xFFFF) {
x = _sceneEnterX3;
y = 191;
@@ -1167,8 +1167,8 @@ void KyraEngine_MR::updateMouse() {
}
if (hasItemCollision && _mouseState < -1 && _itemInHand < 0) {
- _mouseState = -1;
- _itemInHand = -1;
+ _mouseState = kItemNone;
+ _itemInHand = kItemNone;
_screen->setMouseCursor(0, 0, _gameShapes[0]);
}
diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h
index 36b937f2a8..e6f75742f4 100644
--- a/engines/kyra/kyra_mr.h
+++ b/engines/kyra/kyra_mr.h
@@ -235,7 +235,7 @@ private:
void showMessage(const char *string, uint8 c0, uint8 c1);
void showMessageFromCCode(int string, uint8 c0, int);
- void updateItemCommand(int item, int str, uint8 c0);
+ void updateItemCommand(Item item, int str, uint8 c0);
void updateCommandLine();
void restoreCommandLine();
@@ -262,7 +262,7 @@ private:
static const uint8 _inventoryY[];
void redrawInventory(int page);
void clearInventorySlot(int slot, int page);
- void drawInventorySlot(int page, int item, int slot);
+ void drawInventorySlot(int page, Item item, int slot);
WSAMovie_v2 *_invWsa;
int _invWsaFrame;
@@ -284,24 +284,24 @@ private:
int8 *_itemBuffer1;
int8 *_itemBuffer2;
- static const uint8 _trashItemList[];
+ static const Item _trashItemList[];
void removeTrashItems();
void initItems();
int checkItemCollision(int x, int y);
- bool dropItem(int unk1, uint16 item, int x, int y, int unk2);
- bool processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2);
- void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item, int remove);
+ bool dropItem(int unk1, Item item, int x, int y, int unk2);
+ bool processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2);
+ void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item, int remove);
void exchangeMouseItem(int itemPos, int runScript);
bool pickUpItem(int x, int y, int runScript);
bool isDropable(int x, int y);
const uint8 *_itemMagicTable;
- bool itemListMagic(int handItem, int itemSlot);
- bool itemInventoryMagic(int handItem, int invSlot);
+ bool itemListMagic(Item handItem, int itemSlot);
+ bool itemInventoryMagic(Item handItem, int invSlot);
const uint8 *_itemStringMap;
int _itemStringMapSize;
@@ -315,7 +315,7 @@ private:
// -> hand item
void setItemMouseCursor();
- void setMouseCursor(uint16 item);
+ void setMouseCursor(Item item);
// shapes
void initMouseShapes();
@@ -585,7 +585,7 @@ private:
int albumClose(Button *caller);
// save/load
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Common::Error loadGameState(int slot);
// opcodes
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 4d0248b1e4..26c4001578 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -103,11 +103,14 @@ Common::Error KyraEngine_v1::init() {
syncSoundSettings();
if (!_flags.useDigSound) {
- // We prefer AdLib over MIDI in Kyra 1, since it offers MT-32 support only, most users don't have a real
- // MT-32/LAPC1/CM32L/CM64 device and AdLib sounds better than our incomplete MT-32 emulator and also better than
- // MT-32/GM mapping. For Kyra 2 and LoL which have real GM tracks which sound better than AdLib tracks we prefer GM
- // since most users have a GM compatible device.
- MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : 0));
+ // In Kyra 1 users who have specified a default MT-32 device in the launcher settings
+ // will get MT-32 music, otherwise AdLib. In Kyra 2 and LoL users who have specified a
+ // default GM device in the launcher will get GM music, otherwise AdLib. Users who want
+ // MT-32 music in Kyra2 or LoL have to select this individually (since we assume that
+ // most users rather have a GM device than a MT-32 device).
+ // Users who want PC speaker sound always have to select this individually for all
+ // Kyra games.
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : MDT_PREFER_MT32));
if (_flags.platform == Common::kPlatformFMTowns) {
if (_flags.gameID == GI_KYRA1)
@@ -275,7 +278,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
} else {
char savegameName[14];
sprintf(savegameName, "Quicksave %d", event.kbd.keycode - Common::KEYCODE_0);
- saveGameState(saveLoadSlot, savegameName, 0);
+ saveGameStateIntern(saveLoadSlot, savegameName, 0);
}
} else if (event.kbd.hasFlags(Common::KBD_CTRL)) {
if (event.kbd.keycode == Common::KEYCODE_d) {
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index d077d3a3b0..31c07336a6 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -37,6 +37,7 @@
#include "sound/mixer.h"
#include "kyra/script.h"
+#include "kyra/item.h"
namespace Common {
class SeekableReadStream;
@@ -93,7 +94,7 @@ class KyraMetaEngine;
* is pretty minor priority though, since the benefit would be mostly nicer code). The biggest
* task left is the kyra.dat handling, which is currently being revised by LordHoto.
*
- * Supported games:
+ * Games using this engine:
* - The Legend of Kyrandia (fully supported, except for Macintosh port, which lacks sound)
* - (The) Hand of Fate (fully supported)
* - Malcolm's Revenge (fully supported)
@@ -339,7 +340,7 @@ protected:
// items
int _mouseState;
- virtual void setHandItem(uint16 item) = 0;
+ virtual void setHandItem(Item item) = 0;
virtual void removeHandItem() = 0;
// game flags
@@ -414,8 +415,8 @@ protected:
void loadGameStateCheck(int slot);
virtual Common::Error loadGameState(int slot) = 0;
- Common::Error saveGameState(int slot, const char *saveName) { return saveGameState(slot, saveName, 0); }
- virtual Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0;
+ Common::Error saveGameState(int slot, const char *saveName) { return saveGameStateIntern(slot, saveName, 0); }
+ virtual Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0;
Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header);
Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const;
diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h
index 6414040344..39c9f0875e 100644
--- a/engines/kyra/kyra_v2.h
+++ b/engines/kyra/kyra_v2.h
@@ -29,6 +29,7 @@
#include "kyra/kyra_v1.h"
#include "kyra/gui.h"
#include "kyra/wsamovie.h"
+#include "kyra/item.h"
#include "common/list.h"
#include "common/hashmap.h"
@@ -41,7 +42,7 @@ struct FrameControl {
};
struct ItemAnimData_v2 {
- int16 itemIndex;
+ Item itemIndex;
uint8 numFrames;
const FrameControl *frames;
};
@@ -69,7 +70,7 @@ public:
int animScriptFrameAdd;
// Item specific
- int maxItemId;
+ Item maxItemId;
};
KyraEngine_v2(OSystem *system, const GameFlags &flags, const EngineDesc &desc);
@@ -286,8 +287,8 @@ protected:
int _pathfinderPositionIndexTable[200];
// items
- struct Item {
- uint16 id;
+ struct ItemDefinition {
+ Item id;
uint16 sceneId;
int16 x;
uint8 y;
@@ -295,25 +296,26 @@ protected:
void initItemList(int size);
- uint16 _hiddenItems[100];
+ Item _hiddenItems[100];
- Item *_itemList;
+ ItemDefinition *_itemList;
int _itemListSize;
int _itemInHand;
+ int _savedMouseState;
int findFreeItem();
int countAllItems();
- int findItem(uint16 sceneId, uint16 id);
- int findItem(uint16 item);
+ int findItem(uint16 sceneId, Item id);
+ int findItem(Item item);
void resetItemList();
void resetItem(int index);
- virtual void setMouseCursor(uint16 item) = 0;
+ virtual void setMouseCursor(Item item) = 0;
- void setHandItem(uint16 item);
+ void setHandItem(Item item);
void removeHandItem();
// character
@@ -324,7 +326,7 @@ protected:
uint8 facing;
uint16 animFrame;
byte walkspeed;
- uint16 inventory[20];
+ Item inventory[20];
int16 x1, y1;
int16 x2, y2;
int16 x3, y3;
@@ -360,7 +362,7 @@ protected:
virtual void randomSceneChat() = 0;
// unknown
- int _unk3, _unk4, _unk5;
+ int _unk4, _unk5;
bool _unkSceneScreenFlag1;
bool _unkHandleSceneChangeFlag;
diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp
index 03d52ec4ac..f6ac8a1a73 100644
--- a/engines/kyra/lol.cpp
+++ b/engines/kyra/lol.cpp
@@ -1930,11 +1930,11 @@ int LoLEngine::playCharacterScriptChat(int charId, int mode, int restorePortrait
return 1;
}
-void LoLEngine::giveItemToMonster(MonsterInPlay *monster, uint16 item) {
+void LoLEngine::giveItemToMonster(MonsterInPlay *monster, Item item) {
uint16 *c = &monster->assignedItems;
while (*c)
c = &_itemsInPlay[*c].nextAssignedObject;
- *c = item;
+ *c = (uint16)item;
_itemsInPlay[item].nextAssignedObject = 0;
}
diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h
index 57c127a94f..d24f3b427f 100644
--- a/engines/kyra/lol.h
+++ b/engines/kyra/lol.h
@@ -225,7 +225,7 @@ struct FlyingObject {
uint8 enable;
uint8 objectType;
uint16 attackerId;
- uint16 item;
+ Item item;
uint16 x;
uint16 y;
uint8 flyingHeight;
@@ -1202,19 +1202,19 @@ private:
// items
void giveCredits(int credits, int redraw);
void takeCredits(int credits, int redraw);
- int makeItem(int itemType, int curFrame, int flags);
- void placeMoveLevelItem(int itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight);
- bool addItemToInventory(int itemIndex);
- bool testUnkItemFlags(int itemIndex);
- void deleteItem(int itemIndex);
+ Item makeItem(int itemType, int curFrame, int flags);
+ void placeMoveLevelItem(Item itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight);
+ bool addItemToInventory(Item itemIndex);
+ bool testUnkItemFlags(Item itemIndex);
+ void deleteItem(Item itemIndex);
ItemInPlay *findObject(uint16 index);
- void runItemScript(int charNum, int item, int flags, int next, int reg4);
- void setHandItem(uint16 itemIndex);
+ void runItemScript(int charNum, Item item, int flags, int next, int reg4);
+ void setHandItem(Item itemIndex);
bool itemEquipped(int charNum, uint16 itemType);
- void setItemPosition(int item, uint16 x, uint16 y, int flyingHeight, int b);
- void removeLevelItem(int item, int block);
- bool launchObject(int objectType, int item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c);
+ void setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, int b);
+ void removeLevelItem(Item item, int block);
+ bool launchObject(int objectType, Item item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c);
void endObjectFlight(FlyingObject *t, int x, int y, int objectOnNextBlock);
void processObjectFlight(FlyingObject *t, int x, int y);
void updateObjectFlightPosition(FlyingObject *t);
@@ -1223,7 +1223,7 @@ private:
void assignItemToBlock(uint16 *assignedBlockObjects, int id);
int checkDrawObjectSpace(int itemX, int itemY, int partyX, int partyY);
- int checkSceneForItems(uint16 *blockDrawObjects, int colour);
+ int checkSceneForItems(uint16 *blockDrawObjects, int color);
uint8 _moneyColumnHeight[5];
uint16 _credits;
@@ -1231,9 +1231,9 @@ private:
ItemInPlay *_itemsInPlay;
ItemProperty *_itemProperties;
- int _itemInHand;
- uint16 _inventory[48];
- int _inventoryCurItem;
+ Item _itemInHand;
+ Item _inventory[48];
+ Item _inventoryCurItem;
int _currentControlMode;
int _specialSceneFlag;
int _lastCharInventory;
@@ -1269,10 +1269,10 @@ private:
int calcMonsterDirection(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
void setMonsterDirection(MonsterInPlay *monster, int dir);
void monsterDropItems(MonsterInPlay *monster);
- void removeAssignedObjectFromBlock(LevelBlockProperty *l, int id);
- void removeDrawObjectFromBlock(LevelBlockProperty *l, int id);
- void assignMonsterToBlock(uint16 *assignedBlockObjects, int id);
- void giveItemToMonster(MonsterInPlay *monster, uint16 item);
+ void removeAssignedObjectFromBlock(LevelBlockProperty *l, uint16 id);
+ void removeDrawObjectFromBlock(LevelBlockProperty *l, uint16 id);
+ void assignMonsterToBlock(uint16 *assignedBlockObjects, uint16 id);
+ void giveItemToMonster(MonsterInPlay *monster, Item item);
int checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 objectWidth, uint16 testFlag, uint16 wallFlag);
int checkBlockForWallsAndSufficientSpace(int block, int x, int y, int objectWidth, int testFlag, int wallFlag);
int calcMonsterSkillLevel(int id, int a);
@@ -1488,7 +1488,7 @@ private:
// save
Common::Error loadGameState(int slot);
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Graphics::Surface *generateSaveThumbnail() const;
diff --git a/engines/kyra/resource_intern.cpp b/engines/kyra/resource_intern.cpp
index 445ea579a0..f26cb8fb26 100644
--- a/engines/kyra/resource_intern.cpp
+++ b/engines/kyra/resource_intern.cpp
@@ -26,8 +26,9 @@
#include "kyra/resource_intern.h"
#include "kyra/resource.h"
-#include "common/stream.h"
#include "common/endian.h"
+#include "common/memstream.h"
+#include "common/substream.h"
namespace Kyra {
@@ -102,7 +103,7 @@ int TlkArchive::listMembers(Common::ArchiveMemberList &list) {
uint count = 0;
for (; count < _entryCount; ++count) {
- const Common::String name = Common::String::printf("%08u.AUD", _fileEntries[count * 2 + 0]);
+ const Common::String name = Common::String::format("%08u.AUD", _fileEntries[count * 2 + 0]);
list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(name, this)));
}
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
index 3ad7093046..cd5ef52b92 100644
--- a/engines/kyra/saveload.cpp
+++ b/engines/kyra/saveload.cpp
@@ -27,6 +27,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
#include "kyra/kyra_v1.h"
#include "kyra/util.h"
@@ -230,7 +231,7 @@ const char *KyraEngine_v1::getSavegameFilename(int num) {
Common::String KyraEngine_v1::getSavegameFilename(const Common::String &target, int num) {
assert(num >= 0 && num <= 999);
- return target + Common::String::printf(".%03d", num);
+ return target + Common::String::format(".%03d", num);
}
bool KyraEngine_v1::saveFileLoadable(int slot) {
@@ -250,7 +251,7 @@ bool KyraEngine_v1::saveFileLoadable(int slot) {
void KyraEngine_v1::checkAutosave() {
if (shouldPerformAutoSave(_lastAutosave)) {
- saveGameState(999, "Autosave", 0);
+ saveGameStateIntern(999, "Autosave", 0);
_lastAutosave = _system->getMillis();
}
}
diff --git a/engines/kyra/saveload_hof.cpp b/engines/kyra/saveload_hof.cpp
index 2d276cc6a4..ced103b7e3 100644
--- a/engines/kyra/saveload_hof.cpp
+++ b/engines/kyra/saveload_hof.cpp
@@ -25,6 +25,7 @@
#include "common/endian.h"
#include "common/savefile.h"
+#include "common/substream.h"
#include "common/system.h"
#include "kyra/kyra_v2.h"
@@ -35,7 +36,7 @@
namespace Kyra {
-Common::Error KyraEngine_HoF::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumb) {
+Common::Error KyraEngine_HoF::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
@@ -64,7 +65,7 @@ Common::Error KyraEngine_HoF::saveGameState(int slot, const char *saveName, cons
for (int i = 0; i < 25; ++i)
out->writeSint16BE(_cauldronTable[i]);
for (int i = 0; i < 20; ++i)
- out->writeUint16BE(_hiddenItems[i]);
+ out->writeSint16BE(_hiddenItems[i]);
for (int i = 0; i < 19; ++i)
out->write(_conversationState[i], 14);
out->write(_newSceneDlgState, 32);
@@ -83,7 +84,7 @@ Common::Error KyraEngine_HoF::saveGameState(int slot, const char *saveName, cons
out->writeSint16BE(_mainCharacter.y2);
for (int i = 0; i < 30; ++i) {
- out->writeUint16BE(_itemList[i].id);
+ out->writeSint16BE(_itemList[i].id);
out->writeUint16BE(_itemList[i].sceneId);
out->writeSint16BE(_itemList[i].x);
out->writeByte(_itemList[i].y);
@@ -183,7 +184,7 @@ Common::Error KyraEngine_HoF::loadGameState(int slot) {
for (int i = 0; i < 25; ++i)
_cauldronTable[i] = in.readSint16();
for (int i = 0; i < 20; ++i)
- _hiddenItems[i] = in.readUint16();
+ _hiddenItems[i] = in.readSint16();
if (header.originalSave) {
assert(sizeof(_flagsTable) >= 0x41);
@@ -222,7 +223,7 @@ Common::Error KyraEngine_HoF::loadGameState(int slot) {
_mainCharacter.y2 = in.readSint16();
for (int i = 0; i < 30; ++i) {
- _itemList[i].id = in.readUint16();
+ _itemList[i].id = in.readSint16();
_itemList[i].sceneId = in.readUint16();
_itemList[i].x = in.readSint16();
_itemList[i].y = in.readByte();
diff --git a/engines/kyra/saveload_lok.cpp b/engines/kyra/saveload_lok.cpp
index 1a61f9a962..3e11d3dad3 100644
--- a/engines/kyra/saveload_lok.cpp
+++ b/engines/kyra/saveload_lok.cpp
@@ -80,7 +80,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) {
}
_marbleVaseItem = in->readSint16BE();
- _itemInHand = in->readByte();
+ _itemInHand = (int8)in->readByte();
for (int i = 0; i < 4; ++i)
_birthstoneGemTable[i] = in->readByte();
@@ -109,7 +109,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) {
for (int i = 0; i < _roomTableSize; ++i) {
for (int item = 0; item < 12; ++item) {
- _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsTable[item] = kItemNone;
_roomTable[i].itemsXPos[item] = 0xFFFF;
_roomTable[i].itemsYPos[item] = 0xFF;
_roomTable[i].needInit[item] = 0;
@@ -241,7 +241,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) {
return Common::kNoError;
}
-Common::Error KyraEngine_LoK::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumb) {
+Common::Error KyraEngine_LoK::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
if (shouldQuit())
diff --git a/engines/kyra/saveload_lol.cpp b/engines/kyra/saveload_lol.cpp
index 480714e5c9..ee4fbf4dc1 100644
--- a/engines/kyra/saveload_lol.cpp
+++ b/engines/kyra/saveload_lol.cpp
@@ -31,6 +31,7 @@
#include "common/endian.h"
#include "common/savefile.h"
+#include "common/substream.h"
#include "common/system.h"
#include "graphics/scaler.h"
@@ -117,7 +118,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
_selectedCharacter = in.readSByte();
_currentLevel = in.readByte();
for (int i = 0; i < 48; i++)
- _inventory[i] = in.readUint16BE();
+ _inventory[i] = in.readSint16BE();
_inventoryCurItem = in.readSint16BE();
_itemInHand = in.readSint16BE();
_lastMouseRegion = in.readSint16BE();
@@ -243,7 +244,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
m->enable = in.readByte();
m->objectType = in.readByte();
m->attackerId = in.readUint16BE();
- m->item = in.readUint16BE();
+ m->item = in.readSint16BE();
m->x = in.readUint16BE();
m->y = in.readUint16BE();
m->flyingHeight = in.readByte();
@@ -274,7 +275,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
return Common::kNoError;
}
-Common::Error LoLEngine::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
+Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
@@ -340,7 +341,7 @@ Common::Error LoLEngine::saveGameState(int slot, const char *saveName, const Gra
out->writeSByte(_selectedCharacter);
out->writeByte(_currentLevel);
for (int i = 0; i < 48; i++)
- out->writeUint16BE(_inventory[i]);
+ out->writeSint16BE(_inventory[i]);
out->writeSint16BE(_inventoryCurItem);
out->writeSint16BE(_itemInHand);
out->writeSint16BE(_lastMouseRegion);
@@ -423,7 +424,7 @@ Common::Error LoLEngine::saveGameState(int slot, const char *saveName, const Gra
out->writeByte(m->enable);
out->writeByte(m->objectType);
out->writeUint16BE(m->attackerId);
- out->writeUint16BE(m->item);
+ out->writeSint16BE(m->item);
out->writeUint16BE(m->x);
out->writeUint16BE(m->y);
out->writeByte(m->flyingHeight);
diff --git a/engines/kyra/saveload_mr.cpp b/engines/kyra/saveload_mr.cpp
index 737c83c33d..7c583f95ee 100644
--- a/engines/kyra/saveload_mr.cpp
+++ b/engines/kyra/saveload_mr.cpp
@@ -25,6 +25,7 @@
#include "common/endian.h"
#include "common/savefile.h"
+#include "common/substream.h"
#include "common/system.h"
#include "kyra/kyra_mr.h"
@@ -32,7 +33,7 @@
namespace Kyra {
-Common::Error KyraEngine_MR::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumb) {
+Common::Error KyraEngine_MR::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
@@ -55,7 +56,7 @@ Common::Error KyraEngine_MR::saveGameState(int slot, const char *saveName, const
out->write(_conversationState[i], 30);
out->write(_newSceneDlgState, 40);
for (int i = 0; i < 100; ++i)
- out->writeUint16BE(_hiddenItems[i]);
+ out->writeSint16BE(_hiddenItems[i]);
out->write(_scoreFlagTable, 26);
out->writeUint16BE(_mainCharacter.sceneId);
@@ -74,7 +75,7 @@ Common::Error KyraEngine_MR::saveGameState(int slot, const char *saveName, const
out->writeSint16BE(_mainCharacter.y3);
for (int i = 0; i < 50; ++i) {
- out->writeUint16BE(_itemList[i].id);
+ out->writeSint16BE(_itemList[i].id);
out->writeUint16BE(_itemList[i].sceneId);
out->writeSint16BE(_itemList[i].x);
out->writeSint16BE(_itemList[i].y);
@@ -189,7 +190,7 @@ Common::Error KyraEngine_MR::loadGameState(int slot) {
}
for (int i = 0; i < 100; ++i)
- _hiddenItems[i] = in.readUint16();
+ _hiddenItems[i] = in.readSint16();
if (header.originalSave)
in.read(_flagsTable, 69);
@@ -216,7 +217,7 @@ Common::Error KyraEngine_MR::loadGameState(int slot) {
_mainCharacter.y3 = in.readSint16();
for (int i = 0; i < 50; ++i) {
- _itemList[i].id = in.readUint16();
+ _itemList[i].id = in.readSint16();
_itemList[i].sceneId = in.readUint16();
_itemList[i].x = in.readSint16();
_itemList[i].y = in.readSint16();
diff --git a/engines/kyra/scene_hof.cpp b/engines/kyra/scene_hof.cpp
index 4559554d77..79827361a3 100644
--- a/engines/kyra/scene_hof.cpp
+++ b/engines/kyra/scene_hof.cpp
@@ -241,7 +241,7 @@ void KyraEngine_HoF::enterNewSceneUnk1(int facing, int unk1, int unk2) {
}
void KyraEngine_HoF::enterNewSceneUnk2(int unk1) {
- _unk3 = -1;
+ _savedMouseState = -1;
if (_flags.isTalkie) {
if (_mainCharX == -1 && _mainCharY == -1 && _mainCharacter.sceneId != 61 &&
@@ -265,7 +265,7 @@ void KyraEngine_HoF::enterNewSceneUnk2(int unk1) {
}
_unk4 = 0;
- _unk3 = -1;
+ _savedMouseState = -1;
}
int KyraEngine_HoF::trySceneChange(int *moveTable, int unk1, int updateChar) {
@@ -339,16 +339,16 @@ int KyraEngine_HoF::checkSceneChange() {
int facing = 0;
int process = 0;
- if (_screen->getLayer(charX, charY) == 1 && _unk3 == -6) {
+ if (_screen->getLayer(charX, charY) == 1 && _savedMouseState == -6) {
facing = 0;
process = 1;
- } else if (charX >= 316 && _unk3 == -5) {
+ } else if (charX >= 316 && _savedMouseState == -5) {
facing = 2;
process = 1;
- } else if (charY >= 142 && _unk3 == -4) {
+ } else if (charY >= 142 && _savedMouseState == -4) {
facing = 4;
process = 1;
- } else if (charX <= 4 && _unk3 == -3) {
+ } else if (charX <= 4 && _savedMouseState == -3) {
facing = 6;
process = 1;
}
diff --git a/engines/kyra/scene_lok.cpp b/engines/kyra/scene_lok.cpp
index f71c3bd756..f7ada5d623 100644
--- a/engines/kyra/scene_lok.cpp
+++ b/engines/kyra/scene_lok.cpp
@@ -824,13 +824,14 @@ void KyraEngine_LoK::initSceneScreen(int brandonAlive) {
_emc->run(&_scriptClick);
setTextFadeTimerCountdown(-1);
+
if (_currentCharacter->sceneId == 210) {
- if (_itemInHand != -1)
+ if (_itemInHand != kItemNone)
magicOutMouseItem(2, -1);
_screen->hideMouse();
for (int i = 0; i < 10; ++i) {
- if (_currentCharacter->inventoryItems[i] != 0xFF)
+ if (_currentCharacter->inventoryItems[i] != kItemNone)
magicOutMouseItem(2, i);
}
_screen->showMouse();
diff --git a/engines/kyra/scene_lol.cpp b/engines/kyra/scene_lol.cpp
index bf3320486a..e070b91a44 100644
--- a/engines/kyra/scene_lol.cpp
+++ b/engines/kyra/scene_lol.cpp
@@ -1618,7 +1618,7 @@ void LoLEngine::generateBlockDrawingBuffer() {
_sceneDrawVarLeft = _dscBlockMap[_currentDirection + 8];
/*******************************************
- * _visibleBlocks map *
+ * _visibleBlocks map *
* *
* | | | | | | *
* 00 | 01 | 02 | 03 | 04 | 05 | 06 *
diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp
index bd0a1fe544..f1ea79f49b 100644
--- a/engines/kyra/scene_mr.cpp
+++ b/engines/kyra/scene_mr.cpp
@@ -176,8 +176,8 @@ void KyraEngine_MR::enterNewScene(uint16 sceneId, int facing, int unk1, int unk2
setNextIdleAnimTimer();
if (_itemInHand < 0) {
- _itemInHand = -1;
- _mouseState = -1;
+ _itemInHand = kItemNone;
+ _mouseState = kItemNone;
_screen->setMouseCursor(0, 0, _gameShapes[0]);
}
@@ -287,7 +287,7 @@ void KyraEngine_MR::enterNewSceneUnk1(int facing, int unk1, int unk2) {
}
void KyraEngine_MR::enterNewSceneUnk2(int unk1) {
- _unk3 = -1;
+ _savedMouseState = -1;
if (_mainCharX == -1 && _mainCharY == -1 && !unk1) {
_mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
updateCharacterAnim(0);
@@ -300,7 +300,7 @@ void KyraEngine_MR::enterNewSceneUnk2(int unk1) {
}
_unk4 = 0;
- _unk3 = -1;
+ _savedMouseState = -1;
}
void KyraEngine_MR::unloadScene() {
@@ -556,8 +556,8 @@ void KyraEngine_MR::initSceneAnims(int unk1) {
for (int i = 0; i < 50; ++i) {
obj = &_animObjects[i+17];
- const Item &item = _itemList[i];
- if (item.id != 0xFFFF && item.sceneId == _mainCharacter.sceneId) {
+ const ItemDefinition &item = _itemList[i];
+ if (item.id != kItemNone && item.sceneId == _mainCharacter.sceneId) {
obj->xPos1 = item.x;
obj->yPos1 = item.y;
animSetupPaletteEntry(obj);
@@ -696,16 +696,16 @@ int KyraEngine_MR::checkSceneChange() {
int facing = 0;
int process = 0;
- if (_screen->getLayer(charX, charY) == 1 && _unk3 == -7) {
+ if (_screen->getLayer(charX, charY) == 1 && _savedMouseState == -7) {
facing = 0;
process = 1;
- } else if (charX >= 316 && _unk3 == -6) {
+ } else if (charX >= 316 && _savedMouseState == -6) {
facing = 2;
process = 1;
- } else if (charY >= 186 && _unk3 == -5) {
+ } else if (charY >= 186 && _savedMouseState == -5) {
facing = 4;
process = 1;
- } else if (charX <= 4 && _unk3 == -4) {
+ } else if (charX <= 4 && _savedMouseState == -4) {
facing = 6;
process = 1;
}
@@ -742,7 +742,7 @@ int KyraEngine_MR::checkSceneChange() {
return 1;
}
int KyraEngine_MR::runSceneScript1(int x, int y) {
- if (y > 187 && _unk3 > -4)
+ if (y > 187 && _savedMouseState > -4)
return 0;
if (_deathHandler >= 0)
return 0;
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
index b7e01f31aa..c86e043db7 100644
--- a/engines/kyra/screen.cpp
+++ b/engines/kyra/screen.cpp
@@ -25,6 +25,7 @@
#include "common/endian.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "engines/util.h"
@@ -3313,7 +3314,7 @@ SJISFont::SJISFont(Screen *s, Graphics::FontSJIS *font, const uint8 invisColor,
: _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color), _screen(s) {
assert(_font);
- _font->enableOutline(outlineSize);
+ _font->setDrawingMode(outlineSize ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
_sjisWidth = _font->getMaxFontWidth() >> 1;
_fontHeight = _font->getFontHeight() >> 1;
@@ -3345,9 +3346,9 @@ void SJISFont::setColorMap(const uint8 *src) {
if (!_is16Color) {
if (_colorMap[0] == _invisColor)
- _font->enableOutline(false);
+ _font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
else
- _font->enableOutline(true);
+ _font->setDrawingMode(Graphics::FontSJIS::kOutlineMode);
}
}
diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h
index 654b7da6b7..e35b9b37b2 100644
--- a/engines/kyra/screen.h
+++ b/engines/kyra/screen.h
@@ -423,6 +423,7 @@ public:
virtual void setScreenDim(int dim) = 0;
virtual const ScreenDim *getScreenDim(int dim) = 0;
+ virtual int screenDimTableCount() const = 0;
const ScreenDim *_curDim;
diff --git a/engines/kyra/screen_hof.h b/engines/kyra/screen_hof.h
index 1c17a424b3..5117716ac0 100644
--- a/engines/kyra/screen_hof.h
+++ b/engines/kyra/screen_hof.h
@@ -39,6 +39,7 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
+ int screenDimTableCount() const { return _screenDimTableCount; }
// sequence player
void generateGrayOverlay(const Palette &pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool flag);
diff --git a/engines/kyra/screen_lok.h b/engines/kyra/screen_lok.h
index 2a5ef7e12e..0d30c35bfd 100644
--- a/engines/kyra/screen_lok.h
+++ b/engines/kyra/screen_lok.h
@@ -43,6 +43,7 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
+ int screenDimTableCount() const { return _screenDimTableCount; }
void setTextColorMap(const uint8 *cmap);
diff --git a/engines/kyra/screen_lol.h b/engines/kyra/screen_lol.h
index 52e66df1ec..9f4d751d0c 100644
--- a/engines/kyra/screen_lol.h
+++ b/engines/kyra/screen_lol.h
@@ -43,8 +43,9 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
- int curDimIndex() { return _curDimIndex; }
+ int curDimIndex() const { return _curDimIndex; }
void modifyScreenDim(int dim, int x, int y, int w, int h);
+ int screenDimTableCount() const { return _screenDimTableCount; }
void fprintString(const char *format, int x, int y, uint8 col1, uint8 col2, uint16 flags, ...) GCC_PRINTF(2, 8);
void fprintStringIntro(const char *format, int x, int y, uint8 c1, uint8 c2, uint8 c3, uint16 flags, ...) GCC_PRINTF(2, 9);
diff --git a/engines/kyra/screen_mr.h b/engines/kyra/screen_mr.h
index 4107003b12..c02fc4bfb2 100644
--- a/engines/kyra/screen_mr.h
+++ b/engines/kyra/screen_mr.h
@@ -39,6 +39,7 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
+ int screenDimTableCount() const { return _screenDimTableCount; }
int getLayer(int x, int y);
diff --git a/engines/kyra/screen_v2.cpp b/engines/kyra/screen_v2.cpp
index 4d90bf2bab..9c224d1562 100644
--- a/engines/kyra/screen_v2.cpp
+++ b/engines/kyra/screen_v2.cpp
@@ -187,16 +187,22 @@ uint8 *Screen_v2::getPtrToShape(uint8 *shpFile, int shape) {
}
int Screen_v2::getShapeScaledWidth(const uint8 *shpFile, int scale) {
+ if (!shpFile)
+ return 0;
int width = READ_LE_UINT16(shpFile+3);
return (width * scale) >> 8;
}
int Screen_v2::getShapeScaledHeight(const uint8 *shpFile, int scale) {
+ if (!shpFile)
+ return 0;
int height = shpFile[2];
return (height * scale) >> 8;
}
uint16 Screen_v2::getShapeSize(const uint8 *shp) {
+ if (!shp)
+ return 0;
return READ_LE_UINT16(shp+6);
}
diff --git a/engines/kyra/script_hof.cpp b/engines/kyra/script_hof.cpp
index f30f5787e7..d57bb7efc5 100644
--- a/engines/kyra/script_hof.cpp
+++ b/engines/kyra/script_hof.cpp
@@ -410,7 +410,7 @@ int KyraEngine_HoF::o2_countItemsInScene(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemsInScene(%p) (%d)", (const void *)script, stackPos(0));
int count = 0;
for (int i = 0; i < 30; ++i) {
- if (_itemList[i].sceneId == stackPos(0) && _itemList[i].id != 0xFFFF)
+ if (_itemList[i].sceneId == stackPos(0) && _itemList[i].id != kItemNone)
++count;
}
return count;
@@ -1039,7 +1039,7 @@ int KyraEngine_HoF::o2_setColorCodeValue(EMCState *script) {
int KyraEngine_HoF::o2_countItemInstances(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemInstances(%p) (%d)", (const void *)script, stackPos(0));
- uint16 item = stackPos(0);
+ Item item = stackPos(0);
int count = 0;
for (int i = 0; i < 20; ++i) {
@@ -1047,7 +1047,7 @@ int KyraEngine_HoF::o2_countItemInstances(EMCState *script) {
++count;
}
- if (_itemInHand == int16(item))
+ if (_itemInHand == item)
++count;
for (int i = 0; i < 30; ++i) {
@@ -1075,7 +1075,7 @@ int KyraEngine_HoF::o2_removeItemFromScene(EMCState *script) {
const uint16 item = stackPos(1);
for (int i = 0; i < 30; ++i) {
if (_itemList[i].sceneId == scene && _itemList[i].id == item)
- _itemList[i].id = 0xFFFF;
+ _itemList[i].id = kItemNone;
}
return 0;
}
diff --git a/engines/kyra/script_lok.cpp b/engines/kyra/script_lok.cpp
index 1b4a11f793..a2bad8035e 100644
--- a/engines/kyra/script_lok.cpp
+++ b/engines/kyra/script_lok.cpp
@@ -37,6 +37,7 @@
#include "kyra/sound.h"
namespace Kyra {
+
int KyraEngine_LoK::o1_magicInMouseItem(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_magicInMouseItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1));
magicInMouseItem(stackPos(0), stackPos(1), -1);
@@ -203,9 +204,7 @@ int KyraEngine_LoK::o1_getElapsedSeconds(EMCState *script) {
int KyraEngine_LoK::o1_mouseIsPointer(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_mouseIsPointer(%p) ()", (const void *)script);
- if (_itemInHand == -1)
- return 1;
- return 0;
+ return (_itemInHand == kItemNone);
}
int KyraEngine_LoK::o1_runSceneAnimUntilDone(EMCState *script) {
diff --git a/engines/kyra/script_lol.cpp b/engines/kyra/script_lol.cpp
index 1034e86d4e..33fa16a22c 100644
--- a/engines/kyra/script_lol.cpp
+++ b/engines/kyra/script_lol.cpp
@@ -267,6 +267,12 @@ int LoLEngine::olol_setItemProperty(EMCState *script) {
tmp->nameStringId = stackPos(1);
tmp->shpIndex = stackPos(2);
tmp->type = stackPos(3);
+
+ // WORKAROUND for unpatched early floppy versions.
+ // The Vaelan's cube should not be able to be equipped in a weapon slot.
+ if (stackPos(0) == 264 && tmp->type == 5)
+ tmp->type = 0;
+
tmp->itemScriptFunc = stackPos(4);
tmp->might = stackPos(5);
tmp->skill = stackPos(6);
@@ -1065,6 +1071,7 @@ int LoLEngine::olol_createHandItem(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_createHandItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2));
if (_itemInHand)
return 0;
+
setHandItem(makeItem(stackPos(0), stackPos(1), stackPos(2)));
return 1;
}
diff --git a/engines/kyra/script_mr.cpp b/engines/kyra/script_mr.cpp
index ae125b5b99..a9637a6378 100644
--- a/engines/kyra/script_mr.cpp
+++ b/engines/kyra/script_mr.cpp
@@ -216,7 +216,7 @@ int KyraEngine_MR::o3_removeInventoryItemInstances(EMCState *script) {
const int item = stackPos(0);
for (int i = 0; i < 10; ++i) {
if (_mainCharacter.inventory[i] == item)
- _mainCharacter.inventory[i] = 0xFFFF;
+ _mainCharacter.inventory[i] = kItemNone;
}
return 0;
}
@@ -293,7 +293,7 @@ int KyraEngine_MR::o3_updateScore(EMCState *script) {
int KyraEngine_MR::o3_makeSecondChanceSave(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_makeSecondChanceSave(%p) ()", (const void *)script);
- saveGameState(999, "Autosave", 0);
+ saveGameStateIntern(999, "Autosave", 0);
return 0;
}
@@ -594,7 +594,7 @@ int KyraEngine_MR::o3_updateConversations(EMCState *script) {
int KyraEngine_MR::o3_removeItemSlot(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeItemSlot(%p) (%d)", (const void *)script, stackPos(0));
deleteItemAnimEntry(stackPos(0));
- _itemList[stackPos(0)].id = 0xFFFF;
+ _itemList[stackPos(0)].id = kItemNone;
return 1;
}
@@ -642,7 +642,7 @@ int KyraEngine_MR::o3_removeItemInstances(EMCState *script) {
for (int i = 0; i < 10; ++i) {
if (_mainCharacter.inventory[i] == item) {
- _mainCharacter.inventory[i] = 0xFFFF;
+ _mainCharacter.inventory[i] = kItemNone;
++deleted;
}
}
@@ -654,7 +654,7 @@ int KyraEngine_MR::o3_removeItemInstances(EMCState *script) {
for (int i = 0; i < 50; ++i) {
if (_itemList[i].id == item) {
- _itemList[i].id = 0xFFFF;
+ _itemList[i].id = kItemNone;
++deleted;
}
}
diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp
index 61916b92c0..30291c67db 100644
--- a/engines/kyra/script_tim.cpp
+++ b/engines/kyra/script_tim.cpp
@@ -946,7 +946,7 @@ int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, i
}
}
- if (wsaFlags & 7)
+ if (wsa && (wsaFlags & 7))
wsa->displayFrame(0, 0, x, y, 0, 0, 0);
if (wsaFlags & 3) {
diff --git a/engines/kyra/script_v2.cpp b/engines/kyra/script_v2.cpp
index 01f058c383..17e882398e 100644
--- a/engines/kyra/script_v2.cpp
+++ b/engines/kyra/script_v2.cpp
@@ -69,7 +69,7 @@ int KyraEngine_v2::o2_trySceneChange(EMCState *script) {
if (success) {
_emc->init(script, script->dataPtr);
_unk4 = 0;
- _unk3 = -1;
+ _savedMouseState = -1;
_unk5 = 1;
return 0;
} else {
diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp
index 0a3e13bb26..e92866490b 100644
--- a/engines/kyra/sequences_hof.cpp
+++ b/engines/kyra/sequences_hof.cpp
@@ -2002,7 +2002,7 @@ void KyraEngine_HoF::seq_processText() {
outputStr[linePos] = *srcStr;
srcStr++;
}
- outputStr[linePos] = 0;
+ outputStr[linePos] = 0;
if (*srcStr == 0x0d)
srcStr++;
@@ -2130,7 +2130,7 @@ void KyraEngine_HoF::seq_cmpFadeFrame(const char *cmpFile) {
_screen->cmpFadeFrameStep(4, 320, 200, 0, 0, 2, 320, 200, 0, 0, 320, 200, 6);
_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0);
_screen->updateScreen();
- delayUntil(endtime);
+ delayUntil(endtime);
}
_screen->copyPage(4, 0);
diff --git a/engines/kyra/sequences_lol.cpp b/engines/kyra/sequences_lol.cpp
index ac9148e607..e8ea7a9dcb 100644
--- a/engines/kyra/sequences_lol.cpp
+++ b/engines/kyra/sequences_lol.cpp
@@ -1435,7 +1435,7 @@ void LoLEngine::processCredits(char *t, int dimState, int page, int delayTime) {
if (monsterAnimFrame >= 8)
_screen->drawShape(page, doorShape, doorX, doorY, doorSD, (doorSD == 22) ? 0 : 1);
- _screen->drawShape(page, monsterShape, monsterX, monsterY, 0, 0x104 | ((!isRightMonster | (monsterAnimFrame < 20)) ? 0 : 1), _outroShapeTable, 1, _outroMonsterScaleTableX[monsterAnimFrame], _outroMonsterScaleTableY[monsterAnimFrame]);
+ _screen->drawShape(page, monsterShape, monsterX, monsterY, 0, 0x104 | ((!isRightMonster || monsterAnimFrame < 20) ? 0 : 1), _outroShapeTable, 1, _outroMonsterScaleTableX[monsterAnimFrame], _outroMonsterScaleTableY[monsterAnimFrame]);
if (monsterAnimFrame < 8)
_screen->drawShape(page, doorShape, doorX, doorY, doorSD, (doorSD == 22) ? 0 : 1);
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
index 3b0fbbed0a..8b83796d46 100644
--- a/engines/kyra/sound.h
+++ b/engines/kyra/sound.h
@@ -94,7 +94,7 @@ public:
* @param track track number
* @return true if available, false otherwise
*/
- virtual bool hasSoundFile(uint file) { return (fileListEntry(file) != 0); }
+ virtual bool hasSoundFile(uint file) const { return (fileListEntry(file) != 0); }
/**
* Load a specifc sound file for use of
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index 8458d751de..a45972ece7 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -2304,7 +2304,7 @@ void SoundAdLibPC::haltTrack() {
//_vm->_system->delayMillis(3 * 60);
}
-bool SoundAdLibPC::isPlaying() {
+bool SoundAdLibPC::isPlaying() const {
return _driver->callback(7, int(0)) != 0;
}
diff --git a/engines/kyra/sound_adlib.h b/engines/kyra/sound_adlib.h
index 0607e1dd7a..aaca1b9138 100644
--- a/engines/kyra/sound_adlib.h
+++ b/engines/kyra/sound_adlib.h
@@ -76,7 +76,7 @@ public:
void playTrack(uint8 track);
void haltTrack();
- bool isPlaying();
+ bool isPlaying() const;
void playSoundEffect(uint8 track);
diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h
index f8738bc791..bc1c2be1e2 100644
--- a/engines/kyra/sound_intern.h
+++ b/engines/kyra/sound_intern.h
@@ -68,7 +68,7 @@ public:
void playTrack(uint8 track);
void haltTrack();
- bool isPlaying();
+ bool isPlaying() const;
void playSoundEffect(uint8 track);
void stopAllSoundEffects();
diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp
index c24ce5a95b..8982c1cca4 100644
--- a/engines/kyra/sound_midi.cpp
+++ b/engines/kyra/sound_midi.cpp
@@ -683,7 +683,7 @@ void SoundMidiPC::haltTrack() {
_output->deinitSource(0);
}
-bool SoundMidiPC::isPlaying() {
+bool SoundMidiPC::isPlaying() const {
Common::StackLock lock(_mutex);
return _music->isPlaying();
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index 86a1eb228e..f648a23231 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -30,6 +30,8 @@
#include "kyra/sound_intern.h"
#include "kyra/screen.h"
+#include "backends/audiocd/audiocd.h"
+
#include "sound/audiostream.h"
#include "sound/decoders/raw.h"
@@ -85,8 +87,8 @@ void SoundTowns::playTrack(uint8 track) {
const int32 *const tTable = (const int32 *const)cdaData();
int tTableIndex = 3 * track;
- int trackNum = (int) READ_LE_UINT32(&tTable[tTableIndex + 2]);
- int32 loop = (int32) READ_LE_UINT32(&tTable[tTableIndex + 1]);
+ int trackNum = (int)READ_LE_UINT32(&tTable[tTableIndex + 2]);
+ int32 loop = (int32)READ_LE_UINT32(&tTable[tTableIndex + 1]);
if (track == _lastTrack && _musicEnabled)
return;
diff --git a/engines/kyra/sprites_lol.cpp b/engines/kyra/sprites_lol.cpp
index 3b63618d6d..6245ecdd1f 100644
--- a/engines/kyra/sprites_lol.cpp
+++ b/engines/kyra/sprites_lol.cpp
@@ -350,7 +350,7 @@ void LoLEngine::monsterDropItems(MonsterInPlay *monster) {
}
}
-void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, int id) {
+void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, uint16 id) {
uint16 *blockItemIndex = &l->assignedObjects;
ItemInPlay *i = 0;
@@ -367,7 +367,7 @@ void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, int id) {
}
}
-void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, int id) {
+void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, uint16 id) {
uint16 *blockItemIndex = &l->drawObjects;
ItemInPlay *i = 0;
@@ -384,7 +384,7 @@ void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, int id) {
}
}
-void LoLEngine::assignMonsterToBlock(uint16 *assignedBlockObjects, int id) {
+void LoLEngine::assignMonsterToBlock(uint16 *assignedBlockObjects, uint16 id) {
ItemInPlay *t = findObject(id);
t->nextAssignedObject = *assignedBlockObjects;
*assignedBlockObjects = id;
@@ -901,6 +901,9 @@ void LoLEngine::calcSpriteRelPosition(uint16 x1, uint16 y1, int &x2, int &y2, ui
}
void LoLEngine::drawDoor(uint8 *shape, uint8 *doorPalette, int index, int unk2, int w, int h, int flags) {
+ if (!shape)
+ return;
+
uint8 c = _dscDoor1[(_currentDirection << 5) + unk2];
int r = (c / 5) + 5 * _dscDimMap[index];
uint16 d = _dscShapeOvlIndex[r];
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
index a06b488c86..fe4bd2ad71 100644
--- a/engines/kyra/staticres.cpp
+++ b/engines/kyra/staticres.cpp
@@ -42,7 +42,7 @@
namespace Kyra {
-#define RESFILE_VERSION 72
+#define RESFILE_VERSION 73
namespace {
bool checkKyraDat(Common::SeekableReadStream *file) {
@@ -57,7 +57,7 @@ bool checkKyraDat(Common::SeekableReadStream *file) {
uint8 digestCalc[16];
file->seek(0, SEEK_SET);
- if (!Common::md5_file(*file, digestCalc, size))
+ if (!Common::computeStreamMD5(*file, digestCalc, size))
return false;
for (int i = 0; i < 16; ++i)
@@ -209,7 +209,7 @@ bool StaticResource::tryKyraDatLoad() {
return false;
// load the ID map for our game
- const Common::String filenamePattern = Common::String::printf("0%01X%01X%01X000%01X", game, platform, special, lang);
+ const Common::String filenamePattern = Common::String::format("0%01X%01X%01X000%01X", game, platform, special, lang);
Common::SeekableReadStream *idMap = _vm->resource()->createReadStream(filenamePattern);
if (!idMap)
return false;
@@ -328,7 +328,7 @@ bool StaticResource::prefetchId(int id) {
ResData data;
data.id = id;
data.type = dDesc->_value.type;
- Common::SeekableReadStream *fileStream = _vm->resource()->createReadStream(Common::String::printf("%08X", dDesc->_value.filename));
+ Common::SeekableReadStream *fileStream = _vm->resource()->createReadStream(Common::String::format("%08X", dDesc->_value.filename));
if (!fileStream)
return false;
@@ -912,16 +912,7 @@ void KyraEngine_LoK::loadItems() {
_shapes[216 + i] = _screen->encodeShape( (i % 20) * 16, i/20 * 16, 16, 16, 0);
}
- uint32 size;
- uint8 *fileData = _res->fileData("_ITEM_HT.DAT", &size);
- assert(fileData);
-
- for (int i = 0; i < 107; i++) {
- _itemTable[i].height = fileData[i];
- _itemTable[i].unk1 = _itemTable[i].unk2 = 0;
- }
-
- delete[] fileData;
+ _res->loadFileToBuf("_ITEM_HT.DAT", &_itemHtDat, sizeof(_itemHtDat));
}
void KyraEngine_LoK::loadButtonShapes() {
@@ -1866,9 +1857,9 @@ const uint8 KyraEngine_HoF::_cauldronStateTable[] = {
3, 3, 3, 3, 3, 3, 3
};
-const int16 KyraEngine_HoF::_flaskTable[] = {
+const Item KyraEngine_HoF::_flaskTable[] = {
0x19, 0x14, 0x15, 0x16, 0x17, 0x18, 0x34,
- 0x1B, 0x39, 0x1A, 0x3A, 0x4D, 0x72, -1
+ 0x1B, 0x39, 0x1A, 0x3A, 0x4D, 0x72, kItemNone
};
const uint8 KyraEngine_HoF::_rainbowRoomData[] = {
@@ -1964,10 +1955,10 @@ const uint8 KyraEngine_MR::_inventoryY[] = {
0xB2, 0xB2, 0xB2, 0xB2, 0xB2
};
-const uint8 KyraEngine_MR::_trashItemList[] = {
+const Item KyraEngine_MR::_trashItemList[] = {
0x1E, 0x1D, 0x1C, 0x1F, 0x0F, 0x05, 0x04, 0x00,
0x03, 0x22, 0x0B, 0x20, 0x21, 0x10, 0x11, 0x3A,
- 0x39, 0x40, 0x3E, 0x3D, 0x3C, 0x3F, 0xFF
+ 0x39, 0x40, 0x3E, 0x3D, 0x3C, 0x3F, kItemNone
};
const uint8 KyraEngine_MR::_itemStringPickUp[] = {
diff --git a/engines/kyra/text_lol.cpp b/engines/kyra/text_lol.cpp
index 7f9531507c..9f98586303 100644
--- a/engines/kyra/text_lol.cpp
+++ b/engines/kyra/text_lol.cpp
@@ -46,7 +46,9 @@ TextDisplayer_LoL::TextDisplayer_LoL(LoLEngine *vm, Screen_LoL *screen) : _vm(vm
_currentLine = new char[85];
memset(_currentLine, 0, 85);
- for (int i = 0; i < 14; i++){
+ _textDimData = new TextDimData[_screen->screenDimTableCount()];
+
+ for (int i = 0; i < _screen->screenDimTableCount(); i++){
const ScreenDim *d = _screen->getScreenDim(i);
_textDimData[i].color1 = d->unk8;
_textDimData[i].color2 = d->unkA;
@@ -59,6 +61,7 @@ TextDisplayer_LoL::~TextDisplayer_LoL() {
delete[] _buffer;
delete[] _dialogueBuffer;
delete[] _currentLine;
+ delete[] _textDimData;
}
void TextDisplayer_LoL::setupField(bool mode) {
@@ -185,6 +188,7 @@ void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script,
} else {
oldDim = _screen->curDimIndex();
_screen->setScreenDim(dim);
+ _lineCount = 0;
_textDimData[dim].color1 = isPc98 ? 0x33 : 254;
_textDimData[dim].color2 = _screen->_curDim->unkA;
}
@@ -197,6 +201,7 @@ void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script,
displayText(_dialogueBuffer);
_screen->setScreenDim(oldDim);
+ _lineCount = 0;
_screen->setCurPage(cp);
_screen->setFont(of);
@@ -243,6 +248,7 @@ void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) {
displayText(_buffer);
_screen->setScreenDim(od);
+ _lineCount = 0;
if (!(type & 0x8000)) {
if (soundEffect[type])
@@ -373,7 +379,6 @@ void TextDisplayer_LoL::displayText(char *str, ...) {
_tempString2 = 0;
_currentLine[0] = 0;
-
memset(_ctrl, 0, 3);
char c = parseCommand();
@@ -591,13 +596,13 @@ void TextDisplayer_LoL::printLine(char *str) {
int n2 = 0;
int n1 = (w / 4) - 1;
- do {
+ while (n2 < n1 && n2 < s) {
c = str[n2];
uint8 cu = (uint8) c;
if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0))
n2++;
n2++;
- } while (n2 < n1 && n2 < s);
+ }
s = n2;
}
} else {
diff --git a/engines/kyra/text_lol.h b/engines/kyra/text_lol.h
index 488be17cff..1e5bc8884e 100644
--- a/engines/kyra/text_lol.h
+++ b/engines/kyra/text_lol.h
@@ -90,7 +90,7 @@ private:
uint8 line;
};
- TextDimData _textDimData[14];
+ TextDimData *_textDimData;
};
} // End of namespace Kyra
diff --git a/engines/kyra/timer_mr.cpp b/engines/kyra/timer_mr.cpp
index 48fa55519c..0d89decf5a 100644
--- a/engines/kyra/timer_mr.cpp
+++ b/engines/kyra/timer_mr.cpp
@@ -60,7 +60,7 @@ void KyraEngine_MR::timerRunSceneScript7(int arg) {
void KyraEngine_MR::timerFleaDeath(int arg) {
_timer->setCountdown(4, 5400);
- saveGameState(999, "Autosave", 0);
+ saveGameStateIntern(999, "Autosave", 0);
_screen->hideMouse();
_timer->disable(4);
runAnimationScript("FLEADTH1.EMC", 0, 0, 1, 1);
diff --git a/engines/lastexpress/data/animation.cpp b/engines/lastexpress/data/animation.cpp
new file mode 100644
index 0000000000..d105a34f8c
--- /dev/null
+++ b/engines/lastexpress/data/animation.cpp
@@ -0,0 +1,303 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/animation.h"
+
+#include "lastexpress/data/sequence.h"
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/debug.h"
+#include "lastexpress/helpers.h"
+
+#include "common/events.h"
+#include "common/rational.h"
+#include "common/stream.h"
+
+#include "engines/engine.h"
+
+namespace LastExpress {
+
+Animation::Animation() : _stream(NULL), _currentChunk(NULL), _overlay(NULL), _background1(NULL), _background2(NULL), _backgroundCurrent(0), _audio(NULL), _startTime(0), _changed(false), _flag(0) {
+}
+
+Animation::~Animation() {
+ reset();
+}
+
+void Animation::reset() {
+ SAFE_DELETE(_overlay);
+ SAFE_DELETE(_background1);
+ SAFE_DELETE(_background2);
+ SAFE_DELETE(_audio);
+
+ _backgroundCurrent = 0;
+ _chunks.clear();
+
+ _currentChunk = NULL;
+
+ SAFE_DELETE(_stream);
+}
+
+bool Animation::load(Common::SeekableReadStream *stream, int flag) {
+ if (!stream)
+ return false;
+
+ reset();
+
+ // Keep stream for later decoding of animation
+ _stream = stream;
+
+ // Read header to get the number of chunks
+ uint32 numChunks = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of chunks in NIS file: %d", numChunks);
+
+ // Check if there is enough data
+ if (_stream->size() - _stream->pos() < (signed)(numChunks * sizeof(Chunk))) {
+ debugC(2, kLastExpressDebugGraphics, "NIS file seems to be corrupted!");
+ return false;
+ }
+
+ // Read all the chunks
+ for (uint32 i = 0; i < numChunks; ++i) {
+ Chunk chunk;
+ chunk.type = (ChunkType)_stream->readUint16LE();
+ chunk.frame = _stream->readUint16LE();
+ chunk.size = _stream->readUint32LE();
+
+ _chunks.push_back(chunk);
+
+ debugC(9, kLastExpressDebugGraphics, "Chunk Entry: type 0x%.4x, frame=%d, size=%d", chunk.type, chunk.frame, chunk.size);
+ }
+ _currentChunk = _chunks.begin();
+ _changed = false;
+ _startTime = g_engine->_system->getMillis();
+
+ return true;
+}
+
+bool Animation::process() {
+ if (!_currentChunk)
+ error("Animation::process - internal error: the current chunk iterator is invalid!");
+
+ if (_stream == NULL || _chunks.size() == 0)
+ error("Trying to show an animation before loading data");
+
+ // TODO: - subtract the time paused by the GUI
+ // - Re-implement to be closer to the original engine
+ // - Add support for subtitles
+ // - Use engine sound queue instead of our own appendable sound instance
+ int32 currentFrame = (g_engine->_system->getMillis() - _startTime) * 3 / 100;
+
+ // Process all chunks until the current frame
+ while (!_changed && _currentChunk != NULL && currentFrame > _currentChunk->frame && !hasEnded()) {
+ switch(_currentChunk->type) {
+ //TODO: some info chunks are probably subtitle/sync related
+ case kChunkTypeUnknown1:
+ case kChunkTypeUnknown2:
+ case kChunkTypeUnknown5:
+ debugC(9, kLastExpressDebugGraphics | kLastExpressDebugUnknown, " info chunk: type 0x%.4x (size %d)", _currentChunk->type, _currentChunk->size);
+ assert (_currentChunk->frame == 0);
+ //TODO: _currentChunk->size?
+ break;
+
+ case kChunkTypeAudioInfo:
+ debugC(9, kLastExpressDebugGraphics, " audio info: %d blocks", _currentChunk->size);
+ assert (_currentChunk->frame == 0);
+ //TODO: save the size?
+ _audio = new AppendableSound();
+ break;
+
+ case kChunkTypeUnknown4:
+ debugC(9, kLastExpressDebugGraphics | kLastExpressDebugUnknown, " info block 4");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ //TODO unknown type of chunk
+ break;
+
+ case kChunkTypeBackground1:
+ debugC(9, kLastExpressDebugGraphics, " background frame 1 (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _background1;
+ _background1 = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeSelectBackground1:
+ debugC(9, kLastExpressDebugGraphics, " select background 1");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ _backgroundCurrent = 1;
+ break;
+
+ case kChunkTypeBackground2:
+ debugC(9, kLastExpressDebugGraphics, " background frame 2 (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _background2;
+ _background2 = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeSelectBackground2:
+ debugC(9, kLastExpressDebugGraphics, " select background 2");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ _backgroundCurrent = 2;
+ break;
+
+ case kChunkTypeOverlay:
+ debugC(9, kLastExpressDebugGraphics, " overlay frame (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _overlay;
+ _overlay = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeUpdate:
+ case kChunkTypeUpdateTransition:
+ debugC(9, kLastExpressDebugGraphics, " update%s: frame %d", _currentChunk->type == 15 ? "" : " with transition", _currentChunk->frame);
+ assert (_currentChunk->size == 0);
+ _changed = true;
+ break;
+
+ case kChunkTypeAudioData:
+ debugC(9, kLastExpressDebugGraphics, " audio (%d blocks, %d bytes, frame %d)", _currentChunk->size / _soundBlockSize, _currentChunk->size, _currentChunk->frame);
+ processChunkAudio(_stream, *_currentChunk);
+
+ // Synchronize the audio by resetting the start time
+ if (_currentChunk->frame == 0)
+ _startTime = g_engine->_system->getMillis();
+ break;
+
+ case kChunkTypeAudioEnd:
+ debugC(9, kLastExpressDebugGraphics, " audio end: %d blocks", _currentChunk->frame);
+ assert (_currentChunk->size == 0);
+ _audio->finish();
+ //TODO: we need to start the linked sound (.LNK) after the audio from the animation ends
+ break;
+
+ default:
+ error(" UNKNOWN chunk type=%x frame=%d size=%d", _currentChunk->type, _currentChunk->frame, _currentChunk->size);
+ break;
+ }
+ _currentChunk++;
+ }
+
+ return true;
+}
+
+bool Animation::hasEnded() {
+ return _currentChunk == _chunks.end();
+}
+
+Common::Rect Animation::draw(Graphics::Surface *surface) {
+ if (!_overlay)
+ error("Animation::draw - internal error: the current overlay animation frame is invalid!");
+
+ // Paint the background
+ if (_backgroundCurrent == 1 && _background1)
+ _background1->draw(surface);
+ else if (_backgroundCurrent == 2 && _background2)
+ _background2->draw(surface);
+
+ // Paint the overlay
+ _overlay->draw(surface);
+
+ //TODO
+ return Common::Rect();
+}
+
+AnimFrame *Animation::processChunkFrame(Common::SeekableReadStream *in, const Chunk &c) const {
+ assert (c.frame == 0);
+
+ // Create a temporary chunk buffer
+ Common::SeekableReadStream *str = in->readStream(c.size);
+
+ // Read the frame information
+ FrameInfo i;
+ i.read(str, false);
+
+ // Decode the frame
+ AnimFrame *f = new AnimFrame(str, i);
+
+ // Delete the temporary chunk buffer
+ delete str;
+
+ return f;
+}
+
+void Animation::processChunkAudio(Common::SeekableReadStream *in, const Chunk &c) {
+ if (!_audio)
+ error("Animation::processChunkAudio - internal error: the audio stream is invalid!");
+
+ // Skip the Snd header, to queue just the audio blocks
+ uint32 size = c.size;
+ if ((c.size % 739) != 0) {
+ // Read Snd header
+ uint32 header1 = in->readUint32LE();
+ uint16 header2 = in->readUint16LE();
+ warning("Start ADPCM: %d, %d", header1, header2);
+ size -= 6;
+ }
+
+ // Append the current chunk to the Snd
+ _audio->queueBuffer(in->readStream(size));
+}
+
+// TODO: this method will probably go away and be integrated in the main loop
+void Animation::play() {
+ while (!hasEnded() && !g_engine->getEventManager()->shouldQuit() && !g_engine->getEventManager()->shouldRTL()) {
+ process();
+
+ if (_changed) {
+ // Create a temporary surface to merge the overlay with the background
+ Graphics::Surface *s = new Graphics::Surface;
+ s->create(640, 480, 2);
+
+ draw(s);
+
+ // XXX: Update the screen
+ g_system->copyRectToScreen((byte *)s->pixels, s->pitch, 0, 0, s->w, s->h);
+
+ // Free the temporary surface
+ s->free();
+ delete s;
+
+ _changed = false;
+ }
+
+ g_system->updateScreen();
+
+ //FIXME: implement subtitles
+ g_engine->_system->delayMillis(20);
+
+ // Handle right-click to interrupt animations
+ Common::Event ev;
+ while (g_engine->getEventManager()->pollEvent(ev)) {
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ // Stop audio
+ if (_audio)
+ _audio->finish();
+
+ // TODO start LNK file sound?
+ return;
+ }
+ }
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/animation.h b/engines/lastexpress/data/animation.h
new file mode 100644
index 0000000000..9bc2ba99cb
--- /dev/null
+++ b/engines/lastexpress/data/animation.h
@@ -0,0 +1,124 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ANIMATION_H
+#define LASTEXPRESS_ANIMATION_H
+
+/*
+ Animation format (.NIS)
+
+ uint32 {4} - Number of chunks
+
+ // for each chunk
+ uint16 {2} - Type
+ uint16 {2} - Tag
+ uint32 {4} - Size of chunk
+ byte {x} - Data (for "data" chunks: backgrounds, overlay & audio data)
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/array.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class AnimFrame;
+class AppendableSound;
+
+class Animation : public Drawable {
+public:
+ enum FlagType {
+ kFlagDefault = 16384,
+ kFlagProcess = 49152
+ };
+
+ Animation();
+ ~Animation();
+
+ bool load(Common::SeekableReadStream *stream, int flag = kFlagDefault);
+ bool process();
+ bool hasEnded();
+ Common::Rect draw(Graphics::Surface *surface);
+ void play();
+
+private:
+ static const uint32 _soundBlockSize = 739;
+
+ // despite their size field, info chunks don't have a payload
+ enum ChunkType {
+ kChunkTypeNone = 0,
+ kChunkTypeUnknown1 = 1,
+ kChunkTypeUnknown2 = 2,
+ kChunkTypeAudioInfo = 3,
+ kChunkTypeUnknown4 = 4,
+ kChunkTypeUnknown5 = 5,
+ kChunkTypeBackground1 = 10,
+ kChunkTypeSelectBackground1 = 11,
+ kChunkTypeBackground2 = 12,
+ kChunkTypeSelectBackground2 = 13,
+ kChunkTypeOverlay = 20,
+ kChunkTypeUpdate = 21,
+ kChunkTypeUpdateTransition = 22,
+ kChunkTypeSound1 = 30,
+ kChunkTypeSound2 = 31,
+ kChunkTypeAudioData = 32,
+ kChunkTypeAudioEnd = 99
+ };
+
+ struct Chunk {
+ ChunkType type;
+ uint16 frame;
+ uint32 size;
+
+ Chunk() {
+ type = kChunkTypeNone;
+ frame = 0;
+ size = 0;
+ }
+ };
+
+ void reset();
+ AnimFrame *processChunkFrame(Common::SeekableReadStream *in, const Chunk &c) const;
+ void processChunkAudio(Common::SeekableReadStream *in, const Chunk &c);
+
+ Common::SeekableReadStream *_stream;
+ Common::Array<Chunk> _chunks;
+ Common::Array<Chunk>::iterator _currentChunk;
+ AnimFrame *_overlay, *_background1, *_background2;
+ byte _backgroundCurrent;
+ AppendableSound *_audio;
+
+ uint32 _startTime;
+ bool _changed;
+ int _flag;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ANIMATION_H
diff --git a/engines/lastexpress/data/archive.cpp b/engines/lastexpress/data/archive.cpp
new file mode 100644
index 0000000000..ab95c28b1e
--- /dev/null
+++ b/engines/lastexpress/data/archive.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SND
+
+#include "lastexpress/data/archive.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/substream.h"
+
+namespace LastExpress {
+
+HPFArchive::HPFArchive(const Common::String &path) {
+ _filename = path;
+
+ // Open a stream to the archive
+ Common::SeekableReadStream *archive = SearchMan.createReadStreamForMember(_filename);
+ if (!archive) {
+ debugC(2, kLastExpressDebugResource, "Error opening file: %s", path.c_str());
+ return;
+ }
+
+ debugC(2, kLastExpressDebugResource, "Opened archive: %s", path.c_str());
+
+ // Read header to get the number of files
+ uint32 numFiles = archive->readUint32LE();
+ debugC(3, kLastExpressDebugResource, "Number of files in archive: %d", numFiles);
+
+ // Read the list of files
+ for (unsigned int i = 0; i < numFiles; ++i) {
+ char name[13];
+ HPFEntry entry;
+
+ archive->read(&name, sizeof(char) * _archiveNameSize);
+ entry.offset = archive->readUint32LE();
+ entry.size = archive->readUint32LE();
+ entry.isOnHD = archive->readUint16LE();
+
+ // Terminate string
+ name[12] = '\0';
+
+ Common::String filename(name);
+ filename.toLowercase();
+
+ _files[filename] = entry;
+
+ //debugC(9, kLastExpressDebugResource, "File entry: %s (offset:%d - Size: %d - HD: %u)", &name, entry.offset, entry.size, entry.isOnHD);
+ }
+
+ // Close stream
+ delete archive;
+}
+
+bool HPFArchive::hasFile(const Common::String &name) {
+ return (_files.find(name) != _files.end());
+}
+
+int HPFArchive::listMembers(Common::ArchiveMemberList &list) {
+ int numMembers = 0;
+
+ for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(i->_key, this)));
+ numMembers++;
+ }
+
+ return numMembers;
+}
+
+Common::ArchiveMemberPtr HPFArchive::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *HPFArchive::createReadStreamForMember(const Common::String &name) const {
+ FileMap::const_iterator fDesc = _files.find(name);
+ if (fDesc == _files.end())
+ return NULL;
+
+ Common::File *archive = new Common::File();
+ if (!archive->open(_filename)) {
+ delete archive;
+ return NULL;
+ }
+
+ return new Common::SeekableSubReadStream(archive, fDesc->_value.offset * _archiveSectorSize, fDesc->_value.offset * _archiveSectorSize + fDesc->_value.size * _archiveSectorSize, DisposeAfterUse::YES);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/archive.h b/engines/lastexpress/data/archive.h
new file mode 100644
index 0000000000..3860245bc5
--- /dev/null
+++ b/engines/lastexpress/data/archive.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_HPFARCHIVE_H
+#define LASTEXPRESS_HPFARCHIVE_H
+
+/*
+ Archive file format (.HPF)
+
+ uint32 {4} - number of files
+
+ // for each file
+ char {12} - name (zero-terminated)
+ uint32 {4} - offset (expressed in sectors of 2048 bytes)
+ uint32 {4} - size (expressed in sectors of 2048 bytes)
+ byte {2} - file status: 1 = on disk (ie. in HD.HPF), 0 = on CD
+*/
+
+#include "common/archive.h"
+
+namespace LastExpress {
+
+class HPFArchive : public Common::Archive {
+public:
+ HPFArchive(const Common::String &path);
+
+ bool hasFile(const Common::String &name);
+ int listMembers(Common::ArchiveMemberList &list);
+ Common::ArchiveMemberPtr getMember(const Common::String &name);
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+ int count() { return _files.size(); }
+
+private:
+ static const unsigned int _archiveNameSize = 12;
+ static const unsigned int _archiveSectorSize = 2048;
+
+ // File entry
+ struct HPFEntry {
+ uint32 offset; ///< Offset (in sectors of 2048 bytes)
+ uint32 size; ///< Size (in sectors of 2048 bytes)
+ uint16 isOnHD; ///< File location (1: on HD; 0: on CD)
+ };
+
+ typedef Common::HashMap<Common::String, HPFEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+
+ FileMap _files; ///< List of files
+ Common::String _filename; ///< Filename of the archive
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_HPFARCHIVE_H
diff --git a/engines/lastexpress/data/background.cpp b/engines/lastexpress/data/background.cpp
new file mode 100644
index 0000000000..8b0d338f64
--- /dev/null
+++ b/engines/lastexpress/data/background.cpp
@@ -0,0 +1,143 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on Deniz Oezmen's code and Xentax Wiki documentation
+// http://oezmen.eu/
+// http://wiki.xentax.com/index.php/The_Last_Express_BG
+
+#include "lastexpress/data/background.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+Background::Background() : _data(NULL) {
+ memset(&_header, 0, sizeof(BackgroundHeader));
+}
+
+Background::~Background() {
+ delete[] _data;
+}
+
+bool Background::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ delete[] _data;
+
+ // Load Background header
+ _header.posX = stream->readUint32LE();
+ _header.posY = stream->readUint32LE();
+ _header.width = stream->readUint32LE();
+ _header.height = stream->readUint32LE();
+ _header.redSize = stream->readUint32LE();
+ _header.blueSize = stream->readUint32LE();
+ _header.greenSize = stream->readUint32LE();
+
+ debugC(3, kLastExpressDebugGraphics, "Background Info: (%d, %d) - (%d x %d) - (%d, %d, %d)",
+ _header.posX, _header.posY, _header.width, _header.height,
+ _header.redSize, _header.blueSize, _header.greenSize);
+
+ // Load and decompress Background channel data
+ uint32 numPix = _header.width * _header.height;
+ byte *dataR = decodeComponent(stream, _header.redSize, numPix);
+ byte *dataB = decodeComponent(stream, _header.blueSize, numPix);
+ byte *dataG = decodeComponent(stream, _header.greenSize, numPix);
+
+ // Save to pixel buffer
+ // FIXME handle big-endian case
+ _data = new uint16[_header.width * _header.height];
+ for (uint i = 0; i < _header.width * _header.height; i++)
+ _data[i] = (uint16)((dataR[i] << 10) + (dataG[i] << 5) + dataB[i]);
+
+ // Cleanup buffers
+ delete[] dataR;
+ delete[] dataG;
+ delete[] dataB;
+
+ delete stream;
+
+ return true;
+}
+
+Common::Rect Background::draw(Graphics::Surface *surface) {
+ if (!_data) {
+ debugC(2, kLastExpressDebugGraphics, "Trying to show a background before loading data!");
+ return Common::Rect();
+ }
+
+ int i = 0;
+ for (uint16 y = 0; y < _header.height; y++) {
+ for (uint16 x = 0; x < _header.width; x++) {
+ surface->fillRect(Common::Rect((int16)(_header.posX + x), (int16)(_header.posY + y), (int16)(_header.posX + x + 1), (int16)(_header.posY + y + 1)), _data[i]);
+ i ++;
+ }
+ }
+
+ return Common::Rect((int16)_header.posX, (int16)_header.posY, (int16)(_header.posX + _header.width), (int16)(_header.posY + _header.height));
+}
+
+byte *Background::decodeComponent(Common::SeekableReadStream *in, uint32 inSize, uint32 outSize) const {
+ // Create the destination array
+ byte *out = new byte[outSize];
+ if (!out)
+ return NULL;
+
+ // Initialize the decoding
+ uint32 inPos = 0;
+ uint32 outPos = 0;
+
+ // Decode
+ while (inPos < inSize) {
+ byte inByte = in->readByte();
+ inPos++;
+
+ if (inByte < 0x80) {
+ // Direct decompression (RLE)
+ byte len = (inByte >> 5) + 1;
+ byte data = inByte & 0x1f;
+ for (int i = 0; i < len && outPos < outSize; i++)
+ out[outPos++] = data;
+ } else {
+ // Buffer back reference, 4096 byte window
+ // Take inByte and the following value as a big endian
+ // OfsLen while zeroing the first bit
+ uint16 ofsLen = ((inByte & 0x7F) << 8) | in->readByte();
+ inPos++;
+
+ int32 len = (ofsLen >> 12) + 3;
+ int32 hisPos = (int32)(outPos + (ofsLen & 0x0FFF) - 4096);
+ for (int i = 0; i < len && outPos < outSize; i++)
+ out[outPos++] = out[hisPos++];
+ }
+ }
+
+ return out;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/background.h b/engines/lastexpress/data/background.h
new file mode 100644
index 0000000000..994b216ff9
--- /dev/null
+++ b/engines/lastexpress/data/background.h
@@ -0,0 +1,83 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_BACKGROUND_H
+#define LASTEXPRESS_BACKGROUND_H
+
+/*
+ Background file format (.BG)
+
+ header:
+ uint32 {4} - position X on screen
+ uint32 {4} - position Y on screen
+ uint32 {4} - image width
+ uint32 {4} - image height
+ uint32 {4} - red color channel data size
+ uint32 {4} - blue color channel data size
+ uint32 {4} - green color channel data size
+
+ data:
+ byte {x} - red color channel data
+ byte {x} - blue color channel data
+ byte {x} - green color channel data
+*/
+
+#include "lastexpress/drawable.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class Background : public Drawable {
+public:
+ Background();
+ ~Background();
+
+ bool load(Common::SeekableReadStream *stream);
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ struct BackgroundHeader {
+ uint32 posX; ///< position X on screen
+ uint32 posY; ///< position Y on screen
+ uint32 width; ///< image width
+ uint32 height; ///< image height
+ uint32 redSize; ///< red color channel data size
+ uint32 blueSize; ///< blue color channel data size
+ uint32 greenSize; ///< green color channel data size
+ };
+
+ BackgroundHeader _header;
+ uint16 *_data; ///< decoded background data
+
+ byte *decodeComponent(Common::SeekableReadStream *in, uint32 inSize, uint32 outSize) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BACKGROUND_H
diff --git a/engines/lastexpress/data/cursor.cpp b/engines/lastexpress/data/cursor.cpp
new file mode 100644
index 0000000000..3acb8dd4c0
--- /dev/null
+++ b/engines/lastexpress/data/cursor.cpp
@@ -0,0 +1,145 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/data/cursor.h"
+
+#include "lastexpress/lastexpress.h"
+
+#include "common/stream.h"
+#include "common/system.h"
+#include "graphics/cursorman.h"
+
+namespace LastExpress {
+
+Cursor::Cursor() : _current(kCursorMAX) {
+ memset(&_cursors, 0, sizeof(_cursors));
+}
+
+bool Cursor::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Load the whole file to memory
+ Common::SeekableReadStream *data = stream->readStream((uint32) stream->size());
+ delete stream;
+ if (!data)
+ return false;
+
+ // Read the hotspot data
+ for (int i = 0; i < kCursorMAX; i++) {
+ _cursors[i].hotspotX = data->readUint16LE();
+ _cursors[i].hotspotY = data->readUint16LE();
+ debugC(15, kLastExpressDebugCursor | kLastExpressDebugAll,
+ "Cursor %d hotspot x: %d, hotspot y: %d",
+ i, _cursors[i].hotspotX, _cursors[i].hotspotY);
+ }
+
+ // Read the pixel data
+ for (int i = 0; i < kCursorMAX; i++)
+ for (int pix = 0; pix < 32 * 32; pix++)
+ _cursors[i].image[pix] = data->readUint16LE();
+
+ delete data;
+ return true;
+}
+
+void Cursor::show(bool visible) const {
+ CursorMan.showMouse(visible);
+}
+
+bool Cursor::checkStyle(CursorStyle style) const {
+ if (style >= kCursorMAX) {
+ debugC(2, kLastExpressDebugGraphics, "Trying to use an invalid cursor style: was %d, max %d", (int)style, kCursorMAX);
+ return false;
+ }
+
+ return true;
+}
+
+void Cursor::setStyle(CursorStyle style) {
+ if (!checkStyle(style))
+ return;
+
+ if (style == _current)
+ return;
+
+ debugC(10, kLastExpressDebugCursor | kLastExpressDebugAll, "Cursor: setting style: %d", style);
+
+ // Save the new cursor
+ _current = style;
+
+ // Reuse the screen pixel format
+ Graphics::PixelFormat pf = g_system->getScreenFormat();
+ CursorMan.replaceCursor((const byte *)getCursorImage(style),
+ 32, 32, _cursors[style].hotspotX, _cursors[style].hotspotY,
+ 0, 1, &pf);
+}
+
+const uint16 *Cursor::getCursorImage(CursorStyle style) const {
+ if (!checkStyle(style))
+ return NULL;
+
+ return _cursors[style].image;
+}
+
+
+Icon::Icon(CursorStyle style) : _style(style), _x(0), _y(0), _brightness(100) {}
+
+void Icon::setPosition(int16 x, int16 y) {
+ _x = x;
+ _y = y;
+}
+
+void Icon::setBrightness(uint brightness) {
+ assert(brightness <= 100);
+
+ _brightness = (uint8)brightness;
+}
+
+Common::Rect Icon::draw(Graphics::Surface *surface) {
+ const uint16 *image = ((LastExpressEngine *)g_engine)->getCursor()->getCursorImage((CursorStyle)_style);
+ if (!image)
+ return Common::Rect();
+
+ // TODO adjust brightness. The original game seems to be using a table for that (at least in the highlighting case)
+ for (int j = 0; j < 32; j++) {
+ uint16 *s = (uint16 *)surface->getBasePtr(_x, _y + j);
+ for (int i = 0; i < 32; i++) {
+ if (_brightness == 100)
+ *s = *image;
+ else
+ // HACK change color to show highlight
+ *s = (*image & 0x739C) >> 1;
+
+ // Update the image and surface pointers
+ image++;
+ s++;
+ }
+ }
+
+ return Common::Rect(_x, _y, _x + 32, _y + 32);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/cursor.h b/engines/lastexpress/data/cursor.h
new file mode 100644
index 0000000000..0e9556aa6e
--- /dev/null
+++ b/engines/lastexpress/data/cursor.h
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_CURSOR_H
+#define LASTEXPRESS_CURSOR_H
+
+/*
+ Cursor format (CURSORS.TBM)
+
+ style table:
+ (for each cursor)
+ uint16 {2} - hotspot X
+ uint16 {2} - hotspot Y
+
+ data:
+ (for each cursor)
+ uint16 {32*32} - cursor data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "lastexpress/shared.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class Icon : public Drawable {
+public:
+ Icon(CursorStyle style);
+
+ void setPosition(int16 x, int16 y);
+ void setBrightness(uint brightness);
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ CursorStyle _style;
+ int16 _x, _y;
+ uint8 _brightness;
+};
+
+class Cursor {
+public:
+ Cursor();
+
+ bool load(Common::SeekableReadStream *stream);
+ void show(bool visible) const;
+
+ void setStyle(CursorStyle style);
+ CursorStyle getStyle() const { return _current; }
+
+private:
+ // Style
+ CursorStyle _current;
+
+ // Cursors data
+ struct {
+ uint16 image[32 * 32];
+ uint16 hotspotX, hotspotY;
+ } _cursors[kCursorMAX];
+
+ bool checkStyle(CursorStyle style) const;
+ const uint16 *getCursorImage(CursorStyle style) const;
+
+ // Only allow full access for drawing (needed for getCursorImage)
+ friend Common::Rect Icon::draw(Graphics::Surface *surface);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_CURSOR_H
diff --git a/engines/lastexpress/data/font.cpp b/engines/lastexpress/data/font.cpp
new file mode 100644
index 0000000000..99239606ab
--- /dev/null
+++ b/engines/lastexpress/data/font.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/data/font.h"
+
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+Font::Font() : _numGlyphs(0), _glyphs(NULL), _glyphWidths(0) {
+ memset(&_palette, 0, sizeof(_palette));
+ memset(&_charMap, 0, sizeof(_charMap));
+}
+
+Font::~Font() {
+ reset();
+}
+
+void Font::reset() {
+ delete[] _glyphs;
+ delete[] _glyphWidths;
+}
+
+bool Font::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ // Read the palette
+ for (uint i = 0; i < _paletteSize; i++) {
+ _palette[i] = stream->readUint16LE();
+ }
+
+ // Read the character map
+ stream->read(_charMap, _charMapSize);
+
+ // Read the glyphs
+ _numGlyphs = stream->readUint16LE();
+ _glyphs = new byte[_numGlyphs * 18 * 8];
+ stream->read(_glyphs, _numGlyphs * 18 * 8);
+
+ // TODO: Read something else?
+ //uint16 unknown = fontFile->readByte();
+ //warning("unknown = %d", unknown);
+ //warning("pos = %d", fontFile->pos());
+ //warning("left = %d", fontFile->size() - fontFile->pos());
+
+ //while (!fontFile->eos()) {
+ //unknown = fontFile->readByte();
+ //warning("val = %d", unknown);
+ //}
+
+ // Precalculate glyph widths
+ _glyphWidths = new byte[_numGlyphs];
+ for (uint16 i = 0; i < _numGlyphs; i++) {
+ _glyphWidths[i] = getGlyphWidth(i);
+ }
+
+ delete stream;
+
+ return true;
+}
+
+
+uint16 Font::getCharGlyph(uint16 c) const {
+ //warning("%c", c);
+ if (c >= 0x200)
+ error("Express::Font: Invalid character %d", c);
+
+ return _charMap[c];
+}
+
+byte *Font::getGlyphImg(uint16 g) {
+ if (!_glyphs)
+ error("Express::getGlyphImg: Invalid glyphs!");
+
+ if (g >= _numGlyphs)
+ error("Express::getGlyphImg: Invalid glyph %d (%d available)", g, _numGlyphs);
+
+ return _glyphs + g * 18 * 8;
+}
+
+uint8 Font::getGlyphWidth(uint16 g) {
+ byte *p = getGlyphImg(g);
+
+ uint8 maxLineWidth = 0;
+ for (int j = 0; j < 18; j++) {
+ uint8 currentLineWidth = 0;
+ for (uint8 i = 0; i < 16; i++) {
+ byte index;
+ if (i % 2)
+ index = *p & 0xf;
+ else
+ index = *p >> 4;
+ uint16 color = _palette[index];
+ if (color != 0x1f)
+ currentLineWidth = i;
+ if (i % 2)
+ p++;
+ }
+ if (currentLineWidth > maxLineWidth)
+ maxLineWidth = currentLineWidth;
+ }
+
+ return maxLineWidth;
+}
+
+byte *Font::getCharImg(uint16 c) {
+ return getGlyphImg(getCharGlyph(c));
+}
+
+uint8 Font::getCharWidth(uint16 c) const{
+ if (c == 0x20) {
+ // Space is a special case
+ // TODO: this is an arbitrary value
+ return 10;
+ } else {
+ if (!_glyphWidths)
+ error("Express::getCharWidth: Invalid glyphs widths!");
+
+ return _glyphWidths[getCharGlyph(c)];
+ }
+}
+
+uint16 Font::getStringWidth(Common::String str) const {
+ uint16 width = 0;
+ for (uint i = 0; i < str.size(); i++)
+ width += getCharWidth((unsigned) (int)str[i]);
+
+ return width;
+}
+
+uint16 Font::getStringWidth(const uint16 *str, uint16 length) const {
+ uint16 width = 0;
+ for (uint i = 0; i < length; i++)
+ width += getCharWidth(str[i]);
+
+ return width;
+}
+
+void Font::drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+ byte *p = getCharImg(c);
+
+ for (int16 j = 0; j < 18; j++) {
+ for (int16 i = 0; i < 16; i++) {
+ byte index;
+ if (i % 2)
+ index = *p & 0xf;
+ else
+ index = *p >> 4;
+ uint16 color = _palette[index];
+ if (color != 0x1f) {
+ surface->fillRect(Common::Rect(x+i, y+j, x+i+1, y+j+1), color);
+ }
+ if (i % 2)
+ p++;
+ }
+ }
+}
+
+Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str) {
+ int16 currentX = x;
+ for (uint i = 0; i < str.size(); i++) {
+ drawChar(surface, currentX, y, (unsigned) (int)str[i]);
+ currentX += getCharWidth((unsigned) (int)str[i]);
+ }
+
+ return Common::Rect(x, y, x + currentX, y + (int16)_charHeight);
+}
+
+Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, const uint16 *str, uint16 length) {
+ int16 currentX = x;
+ for (uint i = 0; i < length; i++) {
+ drawChar(surface, currentX, y, str[i]);
+ currentX += getCharWidth(str[i]);
+ }
+
+ return Common::Rect(x, y, x + currentX, y + (int16)_charHeight);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/font.h b/engines/lastexpress/data/font.h
new file mode 100644
index 0000000000..d49db35ba5
--- /dev/null
+++ b/engines/lastexpress/data/font.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_FONT_H
+#define LASTEXPRESS_FONT_H
+
+/*
+ Font format (FONT.DAT)
+
+ uint16 {40} - Palette data
+ byte {200} - Character map
+ uint16 {2} - Number of glyphs
+
+ // For each glyph
+ byte {18*8} - Glyph data
+
+ byte {x} - Unknown data (probably just garbage)
+*/
+
+#include "graphics/surface.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class Font {
+public:
+ Font();
+ ~Font();
+
+ bool load(Common::SeekableReadStream *stream);
+ Common::Rect drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str);
+ Common::Rect drawString(Graphics::Surface *surface, int16 x, int16 y, const uint16 *str, uint16 length);
+
+private:
+ static const uint32 _paletteSize = 0x10;
+ static const uint32 _charMapSize = 0x200;
+ static const uint32 _charHeight = 16;
+
+ void reset();
+
+ uint16 getCharGlyph(uint16 c) const;
+ byte *getGlyphImg(uint16 g);
+ uint8 getGlyphWidth(uint16 g);
+ byte *getCharImg(uint16 c);
+ uint8 getCharWidth(uint16 c) const;
+ uint16 getStringWidth(Common::String str) const;
+ uint16 getStringWidth(const uint16 *str, uint16 length) const;
+ void drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+
+ // Font data
+ uint16 _palette[_paletteSize];
+ uint8 _charMap[_charMapSize];
+ uint16 _numGlyphs;
+ byte *_glyphs;
+ uint8 *_glyphWidths;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FONT_H
diff --git a/engines/lastexpress/data/scene.cpp b/engines/lastexpress/data/scene.cpp
new file mode 100644
index 0000000000..5a943982c4
--- /dev/null
+++ b/engines/lastexpress/data/scene.cpp
@@ -0,0 +1,302 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/data/background.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+SceneHotspot::~SceneHotspot() {
+ for (uint i = 0; i < _coords.size(); i++)
+ SAFE_DELETE(_coords[i]);
+
+ _coords.clear();
+}
+
+SceneHotspot *SceneHotspot::load(Common::SeekableReadStream *stream) {
+ SceneHotspot *hs = new SceneHotspot();
+
+ // Rect
+ hs->rect.left = (int16)stream->readUint16LE();
+ hs->rect.right = (int16)stream->readUint16LE();
+ hs->rect.top = (int16)stream->readUint16LE();
+ hs->rect.bottom = (int16)stream->readUint16LE();
+
+ hs->coordsOffset = stream->readUint32LE();
+ hs->scene = (SceneIndex)stream->readUint16LE();
+ hs->location = stream->readByte();
+ hs->action = (Action)stream->readByte();
+ hs->param1 = stream->readByte();
+ hs->param2 = stream->readByte();
+ hs->param3 = stream->readByte();
+ hs->cursor = stream->readByte();
+ hs->next = stream->readUint32LE();
+
+ debugC(10, kLastExpressDebugScenes, "\thotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d, %d)",
+ hs->scene, hs->location, hs->action, hs->param1, hs->param2, hs->param3, hs->cursor, hs->rect.left, hs->rect.top, hs->rect.right, hs->rect.bottom);
+ debugC(10, kLastExpressDebugScenes, "\t coords=%d next=%d ", hs->coordsOffset, hs->next);
+
+ // Read all coords data
+ uint32 offset = hs->coordsOffset;
+ while (offset != 0) {
+
+ SceneCoord *sceneCoord = new SceneCoord;
+
+ stream->seek(offset, SEEK_SET);
+
+ sceneCoord->field_0 = stream->readSint32LE();
+ sceneCoord->field_4 = stream->readSint32LE();
+ sceneCoord->field_8 = stream->readByte();
+ sceneCoord->next = stream->readUint32LE();
+
+ hs->_coords.push_back(sceneCoord);
+
+ offset = sceneCoord->next;
+ }
+
+ return hs;
+}
+
+Common::String SceneHotspot::toString() const {
+ Common::String output = "";
+
+ output += Common::String::format(" hotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d, %d)",
+ scene, location, action, param1, param2, param3, cursor, rect.left, rect.top, rect.right, rect.bottom);
+
+ return output;
+}
+
+bool SceneHotspot::isInside(const Common::Point &point) {
+
+ bool contains = rect.contains(point);
+
+ if (_coords.empty() || !contains)
+ return contains;
+
+ // Checks extended coordinates
+ for (uint i = 0; i < _coords.size(); i++) {
+
+ SceneCoord *sCoord = _coords[i];
+
+ bool cont;
+ if (sCoord->field_8)
+ cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) >= 0;
+ else
+ cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) <= 0;
+
+ if (!cont)
+ return false;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene
+Scene::~Scene() {
+ // Free the hotspots
+ for (int i = 0; i < (int)_hotspots.size(); i++)
+ delete _hotspots[i];
+}
+
+Scene *Scene::load(Common::SeekableReadStream *stream) {
+ Scene *scene = new Scene();
+
+ stream->read(&scene->_name, sizeof(scene->_name));
+ scene->_sig = stream->readByte();
+ scene->entityPosition = (EntityPosition)stream->readUint16LE();
+ scene->location = (Location)stream->readUint16LE();
+ scene->car = (CarIndex)stream->readUint16LE();
+ scene->position = stream->readByte();
+ scene->type = (Type)stream->readByte();
+ scene->param1 = stream->readByte();
+ scene->param2 = stream->readByte();
+ scene->param3 = stream->readByte();
+ scene->_hotspot = stream->readUint32LE();
+
+ return scene;
+}
+
+void Scene::loadHotspots(Common::SeekableReadStream *stream) {
+ if (!_hotspots.empty())
+ return;
+
+ debugC(10, kLastExpressDebugScenes, "Scene: name=%s, sig=%02d, entityPosition=%d, location=%d", _name, _sig, entityPosition, location);
+ debugC(10, kLastExpressDebugScenes, "\tcar=%02d, position=%02d, type=%02d, param1=%02d", car, position, type, param1);
+ debugC(10, kLastExpressDebugScenes, "\tparam2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);
+
+ // Read all hotspots
+ if (_hotspot != 0) {
+ stream->seek((int32)_hotspot, SEEK_SET);
+ SceneHotspot *hotspot = SceneHotspot::load(stream);
+ while (hotspot) {
+ _hotspots.push_back(hotspot);
+
+ if (hotspot->next == 0)
+ break;
+
+ stream->seek((int32)hotspot->next, SEEK_SET);
+ hotspot = SceneHotspot::load(stream);
+ }
+ }
+}
+
+bool Scene::checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot) {
+ bool found = false;
+ int _location = 0;
+
+ for (int i = 0; i < (int)_hotspots.size(); i++) {
+ if (_hotspots[i]->isInside(coord)) {
+ if (_location <= _hotspots[i]->location) {
+ _location = _hotspots[i]->location;
+ *hotspot = _hotspots[i];
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
+SceneHotspot *Scene::getHotspot(uint index) {
+ if (_hotspots.empty())
+ error("Scene::getHotspot: scene does not have any hotspots!");
+
+ if (index >= _hotspots.size())
+ error("Scene::getHotspot: invalid index (was: %d, max: %d)", index, _hotspots.size() - 1);
+
+ return _hotspots[index];
+}
+
+Common::Rect Scene::draw(Graphics::Surface *surface) {
+ // Safety checks
+ Common::Rect rect;
+
+ Common::String sceneName(_name);
+ sceneName.trim();
+ if (sceneName.empty())
+ error("Scene::draw: This scene is not a valid drawing scene!");
+
+ // Load background
+ Background *background = ((LastExpressEngine *)g_engine)->getResourceManager()->loadBackground(sceneName);
+ if (background) {
+ rect = background->draw(surface);
+ delete background;
+ }
+
+ return rect;
+}
+
+Common::String Scene::toString() {
+ Common::String output = "";
+
+ output += Common::String::format("Scene: name=%s, sig=%02d, entityPosition=%d, location=%d\n", _name, _sig, entityPosition, location);
+ output += Common::String::format(" car=%02d, position=%02d, type=%02d, param1=%02d\n", car, position, type, param1);
+ output += Common::String::format(" param2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);
+
+ // Hotspots
+ if (_hotspots.size() != 0) {
+ output += "\nHotspots:\n";
+ for (int i = 0; i < (int)_hotspots.size(); i++)
+ output += _hotspots[i]->toString() + "\n";
+ }
+
+ return output;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SceneLoader
+SceneLoader::SceneLoader() : _stream(NULL) {}
+
+SceneLoader::~SceneLoader() {
+ clear();
+}
+
+void SceneLoader::clear() {
+ // Remove all scenes
+ for (int i = 0; i < (int)_scenes.size(); i++)
+ delete _scenes[i];
+
+ _scenes.clear();
+
+ delete _stream;
+}
+
+bool SceneLoader::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ clear();
+
+ _stream = stream;
+
+ // Read the default scene to get the total number of scenes
+ Scene *header = Scene::load(_stream);
+ if (!header)
+ error("SceneLoader::load: Invalid data file!");
+
+ debugC(2, kLastExpressDebugScenes, " found %d entries", header->entityPosition); /* Header entityPosition is the scene count */
+
+ if (header->entityPosition > 2500) {
+ delete header;
+
+ return false;
+ }
+
+ _scenes.push_back(header);
+
+ // Read all the chunks
+ for (uint i = 0; i < (uint)header->entityPosition; ++i) {
+ Scene *scene = Scene::load(_stream);
+ if (!scene)
+ break;
+
+ _scenes.push_back(scene);
+ }
+
+ return true;
+}
+
+Scene *SceneLoader::get(SceneIndex index) {
+ if (_scenes.empty())
+ return NULL;
+
+ if (index > _scenes.size())
+ return NULL;
+
+ // Load the hotspots if needed
+ _scenes[(int)index]->loadHotspots(_stream);
+
+ return _scenes[(int)index];
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/scene.h b/engines/lastexpress/data/scene.h
new file mode 100644
index 0000000000..7fc9425f28
--- /dev/null
+++ b/engines/lastexpress/data/scene.h
@@ -0,0 +1,249 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SCENE_H
+#define LASTEXPRESS_SCENE_H
+
+/*
+ Scene format (CDTRAIN.DAT)
+
+ (text:00484750)
+ header (24 bytes)
+ char {8} - entry name (null terminated)
+ byte {1} - 0xCD
+ uint16 {2} - number of scenes (for first entry - always 0 after?)
+ uint16 {2} - 11 ??
+ uint16 {2} - car
+ byte {1} - camera position (used to get the proper sequences to show)
+ byte {1} - type
+ byte {1} - param1
+ byte {1} - param2
+ byte {1} - param3
+ uint32 {4} - Offset to hotspot info struct
+
+ probably contains cursor type too / scene index : 0 - 2500 (max)
+
+ hotspot info (24 bytes)
+ uint16 {2} - left
+ uint16 {2} - right
+ uint16 {2} - top
+ uint16 {2} - bottom
+ uint32 {4} - scene coords data
+ uint16 {2} - scene
+ byte {1} - location;
+ byte {1} - action;
+ byte {1} - param1;
+ byte {1} - param2;
+ byte {1} - param3
+ byte {1} - cursor
+ uint32{4} - offset to next hotpost
+
+ coords data (9 bytes)
+ uint32 {4} - ??
+ uint32 {4} - ??
+ byte {1} - ??
+ uint32 {4} - offset to next coords data structure
+
+*/
+
+#include "lastexpress/drawable.h"
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class Scene;
+
+class SceneHotspot {
+public:
+ enum Action {
+ kActionInventory = 1,
+ kActionSavePoint = 2,
+ kActionPlaySound = 3,
+ kActionPlayMusic = 4,
+ kActionKnockOnDoor = 5,
+ kActionCompartment = 6,
+ kActionPlaySounds = 7,
+ kActionPlayAnimation = 8,
+ kActionOpenCloseObject = 9,
+ kActionObjectUpdateLocation2 = 10,
+ kActionSetItemLocation = 11,
+ kAction12 = 12,
+ kActionPickItem = 13,
+ kActionDropItem = 14,
+ kAction15 = 15,
+ kActionEnterCompartment = 16,
+ kActionGetOutsideTrain = 18,
+ kActionSlip = 19,
+ kActionGetInsideTrain = 20,
+ kActionClimbUpTrain = 21,
+ kActionClimbDownTrain = 22,
+ kActionJumpUpDownTrain = 23,
+ kActionUnbound = 24,
+ kAction25 = 25,
+ kAction26 = 26,
+ kAction27 = 27,
+ kActionConcertSitCough = 28,
+ kAction29 = 29,
+ kActionCatchBeetle = 30,
+ kActionExitCompartment = 31,
+ kAction32 = 32,
+ KActionUseWhistle = 33,
+ kActionOpenMatchBox = 34,
+ kActionOpenBed = 35,
+ kActionDialog = 37,
+ kActionEggBox = 38,
+ kAction39 = 39,
+ kActionBed = 40,
+ kAction41 = 41,
+ kAction42 = 42,
+ kActionSwitchChapter = 43,
+ kAction44 = 44
+ };
+
+ struct SceneCoord {
+ int32 field_0;
+ int32 field_4;
+ byte field_8;
+ uint32 next;
+
+ SceneCoord() {
+ field_0 = 0;
+ field_4 = 0;
+ field_8 = 0;
+ next = 0;
+ }
+ };
+
+ Common::Rect rect;
+ uint32 coordsOffset;
+ SceneIndex scene;
+ byte location;
+ Action action;
+ byte param1;
+ byte param2;
+ byte param3;
+ byte cursor;
+ uint32 next;
+
+ SceneHotspot() {}
+ ~SceneHotspot();
+ static SceneHotspot *load(Common::SeekableReadStream *stream);
+
+ bool isInside(const Common::Point &point);
+
+ Common::String toString() const;
+
+private:
+ Common::Array<SceneCoord *> _coords;
+};
+
+class SceneLoader {
+public:
+ SceneLoader();
+ ~SceneLoader();
+
+ bool load(Common::SeekableReadStream *stream);
+ Scene *get(SceneIndex index);
+
+ uint32 count() const { return _scenes.size() - 1; }
+
+private:
+ Common::SeekableReadStream *_stream;
+ Common::Array<Scene *> _scenes;
+
+ void clear();
+};
+
+class Scene : public Drawable {
+public:
+ // Enumerations
+ enum Type {
+ // PreProcess
+ kTypeObject = 1,
+ kTypeItem = 2,
+ kTypeItem2 = 3,
+ kTypeObjectItem = 4,
+ kTypeItem3 = 5,
+ kTypeObjectLocation2 = 6,
+ kTypeCompartments = 7,
+ kTypeCompartmentsItem = 8,
+
+ // PostProcess
+ kTypeList = 128,
+ kTypeSavePointChapter = 129,
+ kTypeLoadBeetleSequences = 130,
+ kTypeGameOver = 131,
+ kTypeReadText = 132,
+ kType133 = 133
+ };
+
+ // Data
+ EntityPosition entityPosition;
+ Location location;
+ CarIndex car;
+ Position position;
+ Type type;
+ byte param1;
+ byte param2;
+ byte param3;
+
+ ~Scene();
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+ // Hotspots
+ Common::Array<SceneHotspot *> *getHotspots() { return &_hotspots; }
+ bool checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot);
+ SceneHotspot *getHotspot(uint index = 0);
+
+ Common::String toString();
+
+private:
+ char _name[8];
+ byte _sig;
+ uint32 _hotspot;
+
+ Scene() {}
+ Common::Array<SceneHotspot *> _hotspots;
+
+ static Scene *load(Common::SeekableReadStream *stream);
+ void loadHotspots(Common::SeekableReadStream *stream);
+
+ void clear();
+
+ // Only allow full access for loading the scene and the hotspots
+ friend bool SceneLoader::load(Common::SeekableReadStream *stream);
+ friend Scene *SceneLoader::get(SceneIndex index);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SCENE_H
diff --git a/engines/lastexpress/data/sequence.cpp b/engines/lastexpress/data/sequence.cpp
new file mode 100644
index 0000000000..2308d70a2b
--- /dev/null
+++ b/engines/lastexpress/data/sequence.cpp
@@ -0,0 +1,484 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+void FrameInfo::read(Common::SeekableReadStream *in, bool isSequence) {
+ // Save the current position
+ int32 basePos = in->pos();
+
+ dataOffset = in->readUint32LE();
+ unknown = in->readUint32LE();
+ paletteOffset = in->readUint32LE();
+ xPos1 = in->readUint32LE();
+ yPos1 = in->readUint32LE();
+ xPos2 = in->readUint32LE();
+ yPos2 = in->readUint32LE();
+ initialSkip = in->readUint32LE();
+ decompressedEndOffset = in->readUint32LE();
+
+ // Read the compression type for NIS files
+ if (!isSequence) {
+ in->seek(basePos + 0x124);
+ } else {
+ hotspot.left = (int16)in->readUint16LE();
+ hotspot.right = (int16)in->readUint16LE();
+ hotspot.top = (int16)in->readUint16LE();
+ hotspot.bottom = (int16)in->readUint16LE();
+ }
+
+ compressionType = in->readByte();
+ subType = (FrameSubType)in->readByte();
+
+ // Sequence information
+ field_2E = in->readByte();
+ keepPreviousFrame = in->readByte();
+ field_30 = in->readByte();
+ field_31 = in->readByte();
+ soundAction = in->readByte();
+ field_33 = in->readByte();
+ position = in->readByte();
+ field_35 = in->readByte();
+ field_36 = in->readUint16LE();
+ field_38 = in->readUint32LE();
+ entityPosition = (EntityPosition)in->readUint16LE();
+ location = in->readUint16LE();
+ next = in->readUint32LE();
+}
+
+
+// AnimFrame
+
+AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f) : _palette(NULL) {
+ _palSize = 1;
+ // TODO: use just the needed rectangle
+ _image.create(640, 480, 1);
+
+ //debugC(6, kLastExpressDebugGraphics, " Offsets: data=%d, unknown=%d, palette=%d", f.dataOffset, f.unknown, f.paletteOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Position: (%d, %d) - (%d, %d)", f.xPos1, f.yPos1, f.xPos2, f.yPos2);
+ //debugC(6, kLastExpressDebugGraphics, " Initial Skip: %d", f.initialSkip);
+ //debugC(6, kLastExpressDebugGraphics, " Decompressed end offset: %d", f.decompressedEndOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Hotspot: (%d, %d) x (%d, %d)\n", f.hotspot.left, f.hotspot.top, f.hotspot.right, f.hotspot.bottom);
+ //debugC(6, kLastExpressDebugGraphics, " Compression type: %u / %u", f.compressionType, f.subType);
+ //debugC(6, kLastExpressDebugGraphics, " Unknown: %u - %u - %u - %u - %u - %u - %u - %d", f.field_2E, f.field_2F, f.field_30, f.field_31, f.field_33, f.field_35, f.field_36, f.field_38);
+ //debugC(6, kLastExpressDebugGraphics, " Sound action: %u", f.soundAction);
+ //debugC(6, kLastExpressDebugGraphics, " Position: %d", f.position);
+ //debugC(6, kLastExpressDebugGraphics, " Entity Position: %d", f.entityPosition);
+ //debugC(6, kLastExpressDebugGraphics, " Location: %d", f.location);
+ //debugC(6, kLastExpressDebugGraphics, " next: %d", f.next);
+
+ switch (f.compressionType) {
+ case 0:
+ // Empty frame
+ break;
+ case 3:
+ decomp3(in, f);
+ break;
+ case 4:
+ decomp4(in, f);
+ break;
+ case 5:
+ decomp5(in, f);
+ break;
+ case 7:
+ decomp7(in, f);
+ break;
+ case 255:
+ decompFF(in, f);
+ break;
+ default:
+ error("Unknown frame compression: %d", f.compressionType);
+ }
+
+ readPalette(in, f);
+ _rect = Common::Rect((int16)f.xPos1, (int16)f.yPos1, (int16)f.xPos2, (int16)f.yPos2);
+ //_rect.debugPrint(0, "Frame rect:");
+}
+
+AnimFrame::~AnimFrame() {
+ _image.free();
+ delete[] _palette;
+}
+
+Common::Rect AnimFrame::draw(Graphics::Surface *s) {
+ byte *inp = (byte *)_image.pixels;
+ uint16 *outp = (uint16 *)s->pixels;
+ for (int i = 0; i < 640 * 480; i++, inp++, outp++) {
+ if (*inp)
+ *outp = _palette[*inp];
+ }
+ return _rect;
+}
+
+void AnimFrame::readPalette(Common::SeekableReadStream *in, const FrameInfo &f) {
+ // Read the palette
+ in->seek((int)f.paletteOffset);
+ _palette = new uint16[_palSize];
+ for (uint32 i = 0; i < _palSize; i++) {
+ _palette[i] = in->readUint16LE();
+ }
+}
+
+void AnimFrame::decomp3(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0x7, 3);
+}
+
+void AnimFrame::decomp4(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0xf, 4);
+}
+
+void AnimFrame::decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ opcode &= 0x3f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x3f;
+ if (opcode & 0x20) {
+ opcode = ((opcode & 0x1f) << 8) + in->readByte();
+ if (opcode & 0x1000) {
+ out += opcode & 0xfff;
+ continue;
+ }
+ }
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & mask;
+ opcode >>= shift;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp5(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (!(opcode & 0x1f)) {
+ opcode = (uint16)((opcode << 3) + in->readByte());
+ if (opcode & 0x400) {
+ // skip these 10 bits
+ out += (opcode & 0x3ff);
+ } else {
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & 0x1f;
+ opcode >>= 5;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp7(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ if (opcode & 0x20) {
+ opcode &= 0x1f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x1f;
+ if (opcode & 0x10) {
+ opcode = ((opcode & 0xf) << 8) + in->readByte();
+ if (opcode & 0x800) {
+ // skip these 11 bits
+ out += (opcode & 0x7ff);
+ continue;
+ }
+ }
+
+ // skip these 4 bits
+ out += opcode + 2;
+ }
+ } else {
+ opcode &= 0x3f;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ }
+ }
+}
+
+void AnimFrame::decompFF(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode < 0x80) {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ } else {
+ if (opcode < 0xf0) {
+ if (opcode < 0xe0) {
+ // copy old part
+ uint32 old = out + ((opcode & 0x7) << 8) + in->readByte() - 2048;
+ opcode = ((opcode >> 3) & 0xf) + 3;
+ for (int i = 0; i < opcode; i++, out++, old++) {
+ p[out] = p[old];
+ }
+ } else {
+ opcode = (opcode & 0xf) + 1;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ out += ((opcode & 0xf) << 8) + in->readByte();
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SEQUENCE
+//////////////////////////////////////////////////////////////////////////
+
+Sequence::~Sequence() {
+ reset();
+}
+
+void Sequence::reset() {
+ _frames.clear();
+ delete _stream;
+ _stream = NULL;
+}
+
+Sequence *Sequence::load(Common::String name, Common::SeekableReadStream *stream, byte field30) {
+ Sequence *sequence = new Sequence(name);
+
+ if (!sequence->load(stream, field30)) {
+ delete sequence;
+ return NULL;
+ }
+
+ return sequence;
+}
+
+bool Sequence::load(Common::SeekableReadStream *stream, byte field30) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ _field30 = field30;
+
+ // Keep stream for later decoding of sequence
+ _stream = stream;
+
+ // Read header to get the number of frames
+ _stream->seek(0);
+ uint32 numframes = _stream->readUint32LE();
+ uint32 unknown = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of frames in sequence: %d / unknown=0x%x", numframes, unknown);
+
+ // Store frames information
+ for (uint i = 0; i < numframes; i++) {
+
+ // Move stream to start of frame
+ _stream->seek((int32)(_sequenceHeaderSize + i * _sequenceFrameSize), SEEK_SET);
+ if (_stream->eos())
+ error("Couldn't seek to the current frame data");
+
+ // Check if there is enough data
+ if ((unsigned)(_stream->size() - _stream->pos()) < _sequenceFrameSize)
+ error("The sequence frame does not have a valid header");
+
+ FrameInfo info;
+ info.read(_stream, true);
+ _frames.push_back(info);
+ }
+
+ _isLoaded = true;
+
+ return true;
+}
+
+FrameInfo *Sequence::getFrameInfo(uint16 index) {
+ if (_frames.size() == 0)
+ error("Trying to decode a sequence before loading its data");
+
+ if (index > _frames.size() - 1)
+ error("Invalid sequence frame requested: %d, max %d", index, _frames.size() - 1);
+
+ return &_frames[index];
+}
+
+AnimFrame *Sequence::getFrame(uint16 index) {
+
+ FrameInfo *frame = getFrameInfo(index);
+
+ if (!frame)
+ return NULL;
+
+ // Skip "invalid" frames
+ if (frame->compressionType == 0)
+ return NULL;
+
+ debugC(9, kLastExpressDebugGraphics, "Decoding sequence %s: frame %d / %d", _name.c_str(), index, _frames.size() - 1);
+
+ return new AnimFrame(_stream, *frame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SequenceFrame
+SequenceFrame::~SequenceFrame() {
+ if (_dispose && _sequence) {
+ delete _sequence;
+ }
+
+ _sequence = NULL;
+}
+
+Common::Rect SequenceFrame::draw(Graphics::Surface *surface) {
+ if (!_sequence || _frame >= _sequence->count())
+ return Common::Rect();
+
+ AnimFrame *f = _sequence->getFrame(_frame);
+ if (!f)
+ return Common::Rect();
+
+ Common::Rect rect = f->draw(surface);
+
+ delete f;
+
+ return rect;
+}
+
+bool SequenceFrame::setFrame(uint16 frame) {
+ if (!_sequence)
+ return false;
+
+ if (frame < _sequence->count()) {
+ _frame = frame;
+ return true;
+ } else
+ return false;
+}
+
+bool SequenceFrame::nextFrame() {
+ return setFrame(_frame + 1);
+}
+
+FrameInfo *SequenceFrame::getInfo() {
+ if (!_sequence)
+ error("SequenceFrame::getFrameInfo: Invalid sequence!");
+
+ return _sequence->getFrameInfo(_frame);
+}
+
+Common::String SequenceFrame::getName() {
+ if (!_sequence)
+ error("SequenceFrame::getName: Invalid sequence!");
+
+ return _sequence->getName();
+}
+
+bool SequenceFrame::equal(const SequenceFrame *other) const {
+ return _sequence->getName() == other->_sequence->getName() && _frame == other->_frame;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/sequence.h b/engines/lastexpress/data/sequence.h
new file mode 100644
index 0000000000..7ad0a57254
--- /dev/null
+++ b/engines/lastexpress/data/sequence.h
@@ -0,0 +1,208 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SEQUENCE_H
+#define LASTEXPRESS_SEQUENCE_H
+
+/*
+ Sequence format (.SEQ / .NIS (frame header & data only))
+
+ uint32 {4} - Number of frames in sequence
+ uint32 {4} - Unknown
+
+ frames headers (68 bytes):
+ // for each frame
+ uint32 {4} - Data offset (from beginning of file)
+ uint32 {4} - Unknown
+ uint32 {4} - Palette offset (from beginning of file)
+ uint32 {4} - Top-left X coordinate
+ uint32 {4} - Top-left Y coordinate
+ uint32 {4} - Bottom-right X coordinate
+ uint32 {4} - Bottom-right Y coordinate
+ uint32 {4} - Initial offset of decompressed data (doubled, since each pixel occupies one color word)
+ uint32 {4} - End of data after decompression
+
+ (for SEQ files only)
+ uint16 {2} - Hotspot left
+ uint16 {2} - Hotspot right
+ uint16 {2} - Hotspot top
+ uint16 {2} - Hotspot bottom
+ byte {1} - Compression type
+ byte {1} - Subtype (determines which set of decompression functions will be called) => 0, 1, 2, 3
+ byte {1} - Unknown
+ byte {1} - Unknown
+ uint16 {2} - Unknown
+ byte {1} - Sound action
+ byte {1} - Unknown
+ uint32 {4} - positionId
+ uint32 {4} - Unknown
+ uint16 {2} - Entity Position
+ uint16 {2} - Location (~z-order)
+ uint32 {4} - Next sequence in the linked list
+
+ (for NIS files: found at 0x124)
+ byte {1} - Compression type
+
+ palette data:
+ uint16 {x} - palette data (max size: 256)
+
+ data
+ byte {x} - compressed image data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+enum FrameSubType {
+ kFrameTypeNone = 0,
+ kFrameType1 = 1,
+ kFrameType2 = 2,
+ kFrameType3 = 3
+};
+
+struct FrameInfo {
+ void read(Common::SeekableReadStream *in, bool isSequence);
+
+ uint32 dataOffset; ///< Data offset (from beginning of file)
+ uint32 unknown; ///< FIXME: unknown data
+ uint32 paletteOffset; ///< Palette offset (from beginning of file)
+ uint32 xPos1; ///< Top-left X coordinate
+ uint32 yPos1; ///< Top-left Y coordinate
+ uint32 xPos2; ///< Bottom-right X coordinate
+ uint32 yPos2; ///< Bottom-right Y coordinate
+ uint32 initialSkip; ///< Initial on-screen offset of decompressed data (doubled, since each pixel occupies one color word)
+ uint32 decompressedEndOffset; ///< End of data after decompression
+
+ // NIS frame headers end here. SEQ frame headers have additional 32 bytes of
+ // data, notably the compression type at the position outlined above in
+ // CompPos_SEQ
+
+ Common::Rect hotspot;
+
+ byte compressionType; ///< Type of frame compression (0x03, 0x04, 0x05, 0x07, 0xFF)
+ FrameSubType subType; ///< Subtype (byte)
+
+ byte field_2E;
+ byte keepPreviousFrame;
+ byte field_30;
+ byte field_31;
+ byte soundAction;
+ byte field_33;
+ Position position;
+ byte field_35;
+ int16 field_36;
+ uint32 field_38;
+ EntityPosition entityPosition;
+ uint16 location;
+ uint32 next;
+};
+
+class AnimFrame : public Drawable {
+public:
+ AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f);
+ ~AnimFrame();
+ Common::Rect draw(Graphics::Surface *s);
+
+private:
+ void decomp3(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp4(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift);
+ void decomp5(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp7(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decompFF(Common::SeekableReadStream *in, const FrameInfo &f);
+ void readPalette(Common::SeekableReadStream *in, const FrameInfo &f);
+
+ Graphics::Surface _image;
+ uint16 _palSize;
+ uint16 *_palette;
+ Common::Rect _rect;
+};
+
+class Sequence {
+public:
+ Sequence(Common::String name) : _stream(NULL), _isLoaded(false), _name(name), _field30(15) {}
+ ~Sequence();
+
+ static Sequence *load(Common::String name, Common::SeekableReadStream *stream = NULL, byte field30 = 15);
+
+ bool load(Common::SeekableReadStream *stream, byte field30 = 15);
+
+ uint16 count() const { return (uint16)_frames.size(); }
+ AnimFrame *getFrame(uint16 index = 0);
+ FrameInfo *getFrameInfo(uint16 index = 0);
+
+ Common::String getName() { return _name; }
+ byte getField30() { return _field30; }
+
+ bool isLoaded() { return _isLoaded; }
+
+private:
+ static const uint32 _sequenceHeaderSize = 8;
+ static const uint32 _sequenceFrameSize = 68;
+
+ void reset();
+
+ Common::Array<FrameInfo> _frames;
+ Common::SeekableReadStream *_stream;
+ bool _isLoaded;
+
+ Common::String _name;
+ byte _field30; // used when copying sequences
+};
+
+class SequenceFrame : public Drawable {
+public:
+ SequenceFrame(Sequence *sequence, uint16 frame = 0, bool dispose = false) : _sequence(sequence), _frame(frame), _dispose(dispose) {}
+ ~SequenceFrame();
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+ bool setFrame(uint16 frame);
+ uint32 getFrame() { return _frame; }
+ bool nextFrame();
+
+ Common::String getName();
+ FrameInfo *getInfo();
+
+ bool equal(const SequenceFrame *other) const;
+
+private:
+ Sequence *_sequence;
+ uint16 _frame;
+ bool _dispose;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SEQUENCE_H
diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
new file mode 100644
index 0000000000..b98e490215
--- /dev/null
+++ b/engines/lastexpress/data/snd.cpp
@@ -0,0 +1,142 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SND
+
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/debug.h"
+
+#include "sound/decoders/adpcm.h"
+#include "sound/audiostream.h"
+#include "common/memstream.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Sound
+//////////////////////////////////////////////////////////////////////////
+SimpleSound::SimpleSound() : _size(0), _blocks(0), _blockSize(0) {}
+
+SimpleSound::~SimpleSound() {
+ stop();
+}
+
+// Stop the sound
+void SimpleSound::stop() const {
+ g_system->getMixer()->stopHandle(_handle);
+}
+
+void SimpleSound::loadHeader(Common::SeekableReadStream *in) {
+ _size = in->readUint32LE();
+ _blocks = in->readUint16LE();
+ debugC(5, kLastExpressDebugSound, " sound header data: size=\"%d\", %d blocks", _size, _blocks);
+
+ assert (_size % _blocks == 0);
+ _blockSize = _size / _blocks;
+}
+
+Audio::AudioStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size) const {
+ return Audio::makeADPCMStream(in, DisposeAfterUse::YES, size, Audio::kADPCMMSImaLastExpress, 44100, 1, _blockSize);
+}
+
+void SimpleSound::play(Audio::AudioStream *as) {
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, as);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// StreamedSound
+//////////////////////////////////////////////////////////////////////////
+StreamedSound::StreamedSound() {}
+StreamedSound::~StreamedSound() {}
+
+bool StreamedSound::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ g_system->getMixer()->stopHandle(_handle);
+
+ loadHeader(stream);
+
+ // Start decoding the input stream
+ Audio::AudioStream *as = makeDecoder(stream, _size);
+
+ // Start playing the decoded audio stream
+ play(as);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// StreamedSound
+//////////////////////////////////////////////////////////////////////////
+AppendableSound::AppendableSound() : SimpleSound() {
+ // Create an audio stream where the decoded chunks will be appended
+ _as = Audio::makeQueuingAudioStream(44100, false);
+ _finished = false;
+
+ // Start playing the decoded audio stream
+ play(_as);
+
+ // Initialize the block size
+ // TODO: get it as an argument?
+ _blockSize = 739;
+}
+
+AppendableSound::~AppendableSound() {
+ finish();
+
+ _as = NULL;
+}
+
+void AppendableSound::queueBuffer(const byte *data, uint32 size) {
+ Common::MemoryReadStream *buffer = new Common::MemoryReadStream(data, size);
+ queueBuffer(buffer);
+}
+
+void AppendableSound::queueBuffer(Common::SeekableReadStream *bufferIn) {
+ if (!_as)
+ error("AppendableSound::queueBuffer - internal error: the audio stream is invalid!");
+
+ // Setup the ADPCM decoder
+ uint32 sizeIn = (uint32)bufferIn->size();
+ Audio::AudioStream *adpcm = makeDecoder(bufferIn, sizeIn);
+
+ // Queue the stream
+ _as->queueAudioStream(adpcm);
+}
+
+void AppendableSound::finish() {
+ if (!_as)
+ error("AppendableSound::queueBuffer - internal error: the audio stream is invalid!");
+
+ if (!_finished)
+ _as->finish();
+
+ _finished = true;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/snd.h b/engines/lastexpress/data/snd.h
new file mode 100644
index 0000000000..f76f8ff6ca
--- /dev/null
+++ b/engines/lastexpress/data/snd.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SND_H
+#define LASTEXPRESS_SND_H
+
+/*
+ Sound format (.SND / .LNK)
+
+ uint32 {4} - data size
+ uint16 {2} - number of blocks
+
+ // for each block
+ int16 {2} - initial sample
+ byte {1} - initial index
+ byte {1} - unused (00)
+ byte {x} - IMA ADPCM sample codes
+*/
+
+#include "sound/mixer.h"
+
+namespace Audio {
+ class AudioStream;
+ class QueuingAudioStream;
+}
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class SimpleSound {
+public:
+ SimpleSound();
+ virtual ~SimpleSound();
+
+ void stop() const;
+
+protected:
+ void loadHeader(Common::SeekableReadStream *in);
+ Audio::AudioStream *makeDecoder(Common::SeekableReadStream *in, uint32 size) const;
+ void play(Audio::AudioStream *as);
+
+ uint32 _size; ///< data size
+ ///< - NIS: size of all blocks, including those located in the matching LNK file
+ ///< - LNK: size of the LNK file itself, including the header
+ ///< - SND: size of all blocks
+ uint16 _blocks; ///< number of blocks
+ uint32 _blockSize;
+ Audio::SoundHandle _handle;
+};
+
+class StreamedSound : public SimpleSound {
+public:
+ StreamedSound();
+ ~StreamedSound();
+
+ bool load(Common::SeekableReadStream *stream);
+};
+
+class AppendableSound : public SimpleSound {
+public:
+ AppendableSound();
+ ~AppendableSound();
+
+ void queueBuffer(const byte *data, uint32 size);
+ void queueBuffer(Common::SeekableReadStream *bufferIn);
+ void finish();
+
+private:
+ Audio::QueuingAudioStream *_as;
+ bool _finished;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SND_H
diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp
new file mode 100644
index 0000000000..953edd1d1a
--- /dev/null
+++ b/engines/lastexpress/data/subtitle.cpp
@@ -0,0 +1,245 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SBE
+
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Subtitle
+//////////////////////////////////////////////////////////////////////////
+class Subtitle {
+public:
+ Subtitle() : _timeStart(0), _timeStop(0), _topLength(0), _topText(NULL),
+ _bottomLength(0), _bottomText(NULL) {}
+ ~Subtitle() { reset(); }
+
+ bool load(Common::SeekableReadStream *in);
+ Common::Rect draw(Graphics::Surface *surface, Font *font);
+
+ uint16 getTimeStart() const { return _timeStart; }
+ uint16 getTimeStop() const { return _timeStop; }
+
+private:
+ uint16 _timeStart; ///< display start time
+ uint16 _timeStop; ///< display stop time
+
+ uint16 _topLength; ///< top line length
+ uint16 *_topText; ///< bottom line length
+
+ uint16 _bottomLength; ///< top line (UTF-16 string)
+ uint16 *_bottomText; ///< bottom line (UTF-16 string)
+
+ void reset();
+};
+
+void Subtitle::reset() {
+ delete[] _topText;
+ delete[] _bottomText;
+ _topText = NULL;
+ _bottomText = NULL;
+}
+
+template<typename T>
+T *newArray(size_t n) {
+ if (n <= (size_t)-1 / sizeof(T))
+ return new T[n];
+
+ // n is too large
+ return NULL;
+}
+
+bool Subtitle::load(Common::SeekableReadStream *in) {
+ reset();
+
+ if (!in)
+ return false;
+
+ // Read the display times
+ _timeStart = in->readUint16LE();
+ _timeStop = in->readUint16LE();
+
+ // Read the text lengths
+ _topLength = in->readUint16LE();
+ _bottomLength = in->readUint16LE();
+
+ // Create the buffers
+ if (_topLength) {
+ _topText = newArray<uint16>(_topLength + 1);
+ if (!_topText)
+ return false;
+
+ _topText[_topLength] = 0;
+ }
+ if (_bottomLength) {
+ _bottomText = newArray<uint16>(_bottomLength + 1);
+ if (!_bottomText)
+ return false;
+
+ _bottomText[_bottomLength] = 0;
+ }
+
+ // Read the texts
+ for (int i = 0; i < _topLength; i++)
+ _topText[i] = in->readUint16LE();
+ for (int i = 0; i < _bottomLength; i++)
+ _bottomText[i] = in->readUint16LE();
+
+ debugC(9, kLastExpressDebugSubtitle, " %d -> %d:", _timeStart, _timeStop);
+ if (_topLength)
+ debugC(9, kLastExpressDebugSubtitle, "\t%ls", (wchar_t *)_topText);
+ if (_bottomLength)
+ debugC(9, kLastExpressDebugSubtitle, "\t%ls", (wchar_t *)_bottomText);
+
+ return true;
+}
+
+Common::Rect Subtitle::draw(Graphics::Surface *surface, Font *font) {
+ Common::Rect rectTop, rectBottom;
+
+ //FIXME find out proper subtitles coordinates (and hope it's hardcoded and not stored in the sequence or animation)
+ rectTop = font->drawString(surface, 100, 100, _topText, _topLength);
+ rectBottom = font->drawString(surface, 100, 300, _bottomText, _bottomLength);
+
+ rectTop.extend(rectBottom);
+
+ return rectTop;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SubtitleManager
+//////////////////////////////////////////////////////////////////////////
+SubtitleManager::SubtitleManager(Font *font) : _font(font), _maxTime(0), _currentIndex(-1), _lastIndex(-1) {}
+
+SubtitleManager::~SubtitleManager() {
+ reset();
+
+ // Zero passed pointers
+ _font = NULL;
+}
+
+void SubtitleManager::reset() {
+ for (int i = 0; i < (int)_subtitles.size(); i++)
+ delete _subtitles[i];
+
+ _subtitles.clear();
+ _currentIndex = -1;
+ _lastIndex = -1;
+}
+
+bool SubtitleManager::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ reset();
+
+ // Read header to get the number of subtitles
+ uint32 numSubtitles = stream->readUint16LE();
+ if (stream->eos())
+ error("Cannot read from subtitle file");
+
+ debugC(3, kLastExpressDebugSubtitle, "Number of subtitles in file: %d", numSubtitles);
+
+ // TODO: Check that stream contain enough data
+ //if (stream->size() < (signed)(numSubtitles * sizeof(SubtitleData))) {
+ //debugC(2, kLastExpressDebugSubtitle, "Subtitle file does not contain valid data!");
+ //return false;
+ //}
+
+ // Read the list of subtitles
+ _maxTime = 0;
+ for (uint i = 0; i < numSubtitles; i++) {
+ Subtitle *subtitle = new Subtitle();
+ if (!subtitle->load(stream)) {
+ // Failed to read this line
+ reset();
+
+ delete subtitle;
+
+ return false;
+ }
+
+ // Update the max time
+ if (subtitle->getTimeStop() > _maxTime)
+ _maxTime = subtitle->getTimeStop();
+
+ _subtitles.push_back(subtitle);
+ }
+
+ delete stream;
+
+ return true;
+}
+
+uint16 SubtitleManager::getMaxTime() const {
+ return _maxTime;
+}
+
+void SubtitleManager::setTime(uint16 time) {
+ _currentIndex = -1;
+
+ // Find the appropriate line to show
+ for (int16 i = 0; i < (int16)_subtitles.size(); i++) {
+ if ((time >= _subtitles[i]->getTimeStart()) && (time <= _subtitles[i]->getTimeStop())) {
+ // Keep the index of the line to show
+ _currentIndex = i;
+ return;
+ }
+ }
+}
+
+bool SubtitleManager::hasChanged() const {
+ // TODO: mark the old line rect as dirty
+ if (_currentIndex != _lastIndex)
+ return true;
+ else
+ return false;
+}
+
+Common::Rect SubtitleManager::draw(Graphics::Surface *surface) {
+ // Update the last drawn index
+ _lastIndex = _currentIndex;
+
+ // Return if we don't have to draw any line
+ if (_currentIndex == -1)
+ return Common::Rect();
+
+ // Draw the current line
+ assert(_currentIndex >= 0 && _currentIndex < (int16)_subtitles.size());
+ return _subtitles[_currentIndex]->draw(surface, _font);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/subtitle.h b/engines/lastexpress/data/subtitle.h
new file mode 100644
index 0000000000..6d8e7535cb
--- /dev/null
+++ b/engines/lastexpress/data/subtitle.h
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SUBTITLE_H
+#define LASTEXPRESS_SUBTITLE_H
+
+/*
+ Subtitle format (.SBE)
+
+ uint16 {2} - number of subtitles
+
+ // for each subtitle
+ uint16 {2} - display start time
+ uint16 {2} - display stop time
+ uint16 {2} - top line length
+ uint16 {2} - bottom line length
+ byte {x} - top line (UTF-16 string)
+ byte {x} - bottom line (UTF-16 string)
+
+ Subtitles seem to be drawn on screen at (80, 420) x (560, 458)
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/array.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace LastExpress {
+
+class Font;
+class Subtitle;
+
+class SubtitleManager : public Drawable {
+public:
+ SubtitleManager(Font *font);
+ ~SubtitleManager();
+
+ bool load(Common::SeekableReadStream *stream);
+ uint16 getMaxTime() const;
+ void setTime(uint16 time);
+ bool hasChanged() const;
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ Common::Array<Subtitle *> _subtitles;
+ Font *_font;
+ uint16 _maxTime;
+
+ int16 _currentIndex;
+ int16 _lastIndex;
+
+ void reset();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SUBTITLE_H
diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp
new file mode 100644
index 0000000000..520b4cfee3
--- /dev/null
+++ b/engines/lastexpress/debug.cpp
@@ -0,0 +1,1177 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/debug.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/background.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/md5.h"
+
+namespace LastExpress {
+
+Debugger::Debugger(LastExpressEngine *engine) : _engine(engine), _command(NULL), _numParams(0), _commandParams(NULL) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Register the debugger commands
+
+ // General
+ DCmd_Register("help", WRAP_METHOD(Debugger, cmdHelp));
+
+ // Data
+ DCmd_Register("ls", WRAP_METHOD(Debugger, cmdListFiles));
+ DCmd_Register("dump", WRAP_METHOD(Debugger, cmdDumpFiles));
+
+ DCmd_Register("showframe", WRAP_METHOD(Debugger, cmdShowFrame));
+ DCmd_Register("showbg", WRAP_METHOD(Debugger, cmdShowBg));
+ DCmd_Register("playseq", WRAP_METHOD(Debugger, cmdPlaySeq));
+ DCmd_Register("playsnd", WRAP_METHOD(Debugger, cmdPlaySnd));
+ DCmd_Register("playsbe", WRAP_METHOD(Debugger, cmdPlaySbe));
+ DCmd_Register("playnis", WRAP_METHOD(Debugger, cmdPlayNis));
+
+ // Scene & interaction
+ DCmd_Register("loadscene", WRAP_METHOD(Debugger, cmdLoadScene));
+ DCmd_Register("fight", WRAP_METHOD(Debugger, cmdFight));
+ DCmd_Register("beetle", WRAP_METHOD(Debugger, cmdBeetle));
+
+ // Game
+ DCmd_Register("delta", WRAP_METHOD(Debugger, cmdTimeDelta));
+ DCmd_Register("time", WRAP_METHOD(Debugger, cmdTime));
+ DCmd_Register("show", WRAP_METHOD(Debugger, cmdShow));
+ DCmd_Register("entity", WRAP_METHOD(Debugger, cmdEntity));
+
+ // Misc
+ DCmd_Register("loadgame", WRAP_METHOD(Debugger, cmdLoadGame));
+ DCmd_Register("chapter", WRAP_METHOD(Debugger, cmdSwitchChapter));
+ DCmd_Register("clear", WRAP_METHOD(Debugger, cmdClear));
+
+ resetCommand();
+
+ _soundStream = new StreamedSound();
+}
+
+Debugger::~Debugger() {
+ DebugMan.clearAllDebugChannels();
+
+ SAFE_DELETE(_soundStream);
+ resetCommand();
+
+ _command = NULL;
+ _commandParams = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helper functions
+//////////////////////////////////////////////////////////////////////////
+bool Debugger::hasCommand() const {
+ return (_numParams != 0);
+}
+
+void Debugger::resetCommand() {
+ SAFE_DELETE(_command);
+
+ if (_commandParams)
+ for (int i = 0; i < _numParams; i++)
+ free(_commandParams[i]);
+
+ free(_commandParams);
+ _commandParams = NULL;
+ _numParams = 0;
+}
+
+int Debugger::getNumber(const char *arg) const {
+ return strtol(arg, (char **)NULL, 0);
+}
+
+void Debugger::copyCommand(int argc, const char **argv) {
+ _commandParams = (char **)malloc(sizeof(char *) * (uint)argc);
+ if (!_commandParams)
+ return;
+
+ _numParams = argc;
+
+ for (int i = 0; i < _numParams; i++) {
+ _commandParams[i] = (char *)malloc(strlen(argv[i]) + 1);
+ memset(_commandParams[i], 0, strlen(argv[i]) + 1);
+ strcpy(_commandParams[i], argv[i]);
+ }
+
+ // Exit the debugger!
+ Cmd_Exit(0, 0);
+}
+
+void Debugger::callCommand() {
+ if (_command)
+ (*_command)(_numParams, const_cast<const char **>(_commandParams));
+}
+
+void Debugger::loadArchive(ArchiveIndex index) const {
+ _engine->getResourceManager()->loadArchive(index);
+ getScenes()->loadSceneDataFile(index);
+}
+
+// Restore loaded archive
+void Debugger::restoreArchive() const {
+
+ ArchiveIndex index = kArchiveCd1;
+
+ switch (getProgress().chapter) {
+ default:
+ case kChapter1:
+ index = kArchiveCd1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ index = kArchiveCd2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ index = kArchiveCd3;
+ break;
+ }
+
+ _engine->getResourceManager()->loadArchive(index);
+ getScenes()->loadSceneDataFile(index);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Debugger commands
+//////////////////////////////////////////////////////////////////////////
+bool Debugger::cmdHelp(int, const char **) {
+ DebugPrintf("Debug flags\n");
+ DebugPrintf("-----------\n");
+ 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("\n");
+ DebugPrintf("Commands\n");
+ DebugPrintf("--------\n");
+ DebugPrintf(" ls - list files in the archive\n");
+ DebugPrintf(" dump - dump a list of files in all archives\n");
+ DebugPrintf("\n");
+ DebugPrintf(" showframe - show a frame from a sequence\n");
+ DebugPrintf(" showbg - show a background\n");
+ DebugPrintf(" playseq - play a sequence\n");
+ DebugPrintf(" playsnd - play a sound\n");
+ DebugPrintf(" playsbe - play a subtitle\n");
+ DebugPrintf(" playnis - play an animation\n");
+ DebugPrintf("\n");
+ DebugPrintf(" loadscene - load a scene\n");
+ DebugPrintf(" fight - start a fight\n");
+ DebugPrintf(" beetle - start the beetle game\n");
+ DebugPrintf("\n");
+ DebugPrintf(" delta - Adjust the time delta\n");
+ DebugPrintf(" show - show game data\n");
+ DebugPrintf(" entity - show entity data\n");
+ DebugPrintf("\n");
+ DebugPrintf(" loadgame - load a saved game\n");
+ DebugPrintf(" chapter - switch to a specific chapter\n");
+ DebugPrintf(" clear - clear the screen\n");
+ DebugPrintf("\n");
+ return true;
+}
+
+/**
+ * Command: list files in archive
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdListFiles(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filter(const_cast<char *>(argv[1]));
+
+ // Load the proper archive
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ Common::ArchiveMemberList list;
+ int count = _engine->getResourceManager()->listMatchingMembers(list, filter);
+
+ DebugPrintf("Number of matches: %d\n", count);
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it)
+ DebugPrintf(" %s\n", (*it)->getName().c_str());
+
+ // Restore archive
+ if (argc == 3)
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: ls <filter> (use * for all) (<cd number>)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Dump the list of files in the archive
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdDumpFiles(int argc, const char **) {
+#define OUTPUT_ARCHIVE_FILES(name, filename) { \
+ _engine->getResourceManager()->reset(); \
+ _engine->getResourceManager()->loadArchive(filename); \
+ Common::ArchiveMemberList list; \
+ int count = _engine->getResourceManager()->listMatchingMembers(list, "*"); \
+ debugC(1, kLastExpressDebugResource, "\n\n--------------------------------------------------------------------\n"); \
+ debugC(1, kLastExpressDebugResource, "-- " #name " (%d files)\n", count); \
+ debugC(1, kLastExpressDebugResource, "--------------------------------------------------------------------\n\n"); \
+ debugC(1, kLastExpressDebugResource, "Filename,Size,MD5\n"); \
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) { \
+ Common::SeekableReadStream *stream = getArchive((*it)->getName()); \
+ if (!stream) { \
+ DebugPrintf("ERROR: Cannot create stream for file: %s\n", (*it)->getName().c_str()); \
+ restoreArchive(); \
+ return true; \
+ } \
+ Common::String md5str = Common::computeStreamMD5AsString(*stream); \
+ debugC(1, kLastExpressDebugResource, "%s, %d, %s", (*it)->getName().c_str(), stream->size(), md5str.c_str()); \
+ delete stream; \
+ } \
+}
+
+ if (argc == 1) {
+ // For each archive file, dump the list of files
+ if (_engine->isDemo()) {
+ OUTPUT_ARCHIVE_FILES("DEMO", "DEMO.HPF");
+ } else {
+ OUTPUT_ARCHIVE_FILES("HD", "HD.HPF");
+ OUTPUT_ARCHIVE_FILES("CD 1", "CD1.HPF");
+ OUTPUT_ARCHIVE_FILES("CD 2", "CD2.HPF");
+ OUTPUT_ARCHIVE_FILES("CD 3", "CD3.HPF");
+ }
+
+ // Restore current loaded archive
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: dump");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Shows a frame
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShowFrame(int argc, const char **argv) {
+ if (argc == 3 || argc == 4) {
+ Common::String filename(const_cast<char *>(argv[1]));
+ filename += ".seq";
+
+ if (argc == 4)
+ loadArchive((ArchiveIndex)getNumber(argv[3]));
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdShowFrame);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ Sequence sequence(filename);
+ if (sequence.load(getArchive(filename))) {
+ _engine->getCursor()->show(false);
+ clearBg(GraphicsManager::kBackgroundOverlay);
+
+ AnimFrame *frame = sequence.getFrame((uint16)getNumber(argv[2]));
+ if (!frame) {
+ DebugPrintf("Invalid frame index '%s'\n", argv[2]);
+ resetCommand();
+ return true;
+ }
+
+ _engine->getGraphicsManager()->draw(frame, GraphicsManager::kBackgroundOverlay);
+ delete frame;
+
+ askForRedraw();
+ redrawScreen();
+
+ _engine->_system->delayMillis(1000);
+ _engine->getCursor()->show(true);
+ }
+
+ resetCommand();
+
+ if (argc == 4)
+ restoreArchive();
+ }
+ } else {
+ DebugPrintf("Syntax: cmd_showframe <seqname> <index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: shows a background
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShowBg(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (!_engine->getResourceManager()->hasFile(filename + ".BG")) {
+ DebugPrintf("Cannot find file: %s\n", (filename + ".BG").c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdShowBg);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ clearBg(GraphicsManager::kBackgroundC);
+
+ Background *background = _engine->getResourceManager()->loadBackground(filename);
+ if (background) {
+ _engine->getGraphicsManager()->draw(background, GraphicsManager::kBackgroundC);
+ delete background;
+ askForRedraw();
+ }
+
+ redrawScreen();
+
+ if (argc == 3)
+ restoreArchive();
+
+ // Pause for a second to be able to see the background
+ _engine->_system->delayMillis(1000);
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: showbg <bgname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a sequence.
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySeq(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+ filename += ".seq";
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlaySeq);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ Sequence *sequence = new Sequence(filename);
+ if (sequence->load(getArchive(filename))) {
+
+ // Check that we have at least a frame to show
+ if (sequence->count() == 0) {
+ delete sequence;
+ return false;
+ }
+
+ _engine->getCursor()->show(false);
+
+ SequenceFrame player(sequence, 0, true);
+ do {
+ // Clear screen
+ clearBg(GraphicsManager::kBackgroundA);
+
+ _engine->getGraphicsManager()->draw(&player, GraphicsManager::kBackgroundA);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Handle right-click to interrupt sequence
+ Common::Event ev;
+ _engine->getEventManager()->pollEvent(ev);
+ if (ev.type == Common::EVENT_RBUTTONUP)
+ break;
+
+ _engine->_system->delayMillis(175);
+
+ // go to the next frame
+ } while (player.nextFrame());
+ _engine->getCursor()->show(true);
+ } else {
+ // Sequence player is deleting his reference to the sequence, but we need to take care of it if the
+ // sequence could not be loaded
+ delete sequence;
+ }
+
+ resetCommand();
+
+ if (argc == 3)
+ restoreArchive();
+ }
+ } else {
+ DebugPrintf("Syntax: playseq <seqname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a sound
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySnd(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ // Add .SND at the end of the filename if needed
+ Common::String name(const_cast<char *>(argv[1]));
+ if (!name.contains('.'))
+ name += ".SND";
+
+ if (!_engine->getResourceManager()->hasFile(name)) {
+ DebugPrintf("Cannot find file: %s\n", name.c_str());
+ return true;
+ }
+
+ _engine->_system->getMixer()->stopAll();
+
+ _soundStream->load(getArchive(name));
+
+ if (argc == 3)
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: playsnd <sndname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays subtitles
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySbe(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ filename += ".sbe";
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlaySbe);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ SubtitleManager subtitle(_engine->getFont());
+ if (subtitle.load(getArchive(filename))) {
+ _engine->getCursor()->show(false);
+ for (uint16 i = 0; i < subtitle.getMaxTime(); i += 25) {
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ subtitle.setTime(i);
+ _engine->getGraphicsManager()->draw(&subtitle, GraphicsManager::kBackgroundOverlay);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Handle right-click to interrupt sequence
+ Common::Event ev;
+ _engine->getEventManager()->pollEvent(ev);
+ if (ev.type == Common::EVENT_RBUTTONUP)
+ break;
+
+ _engine->_system->delayMillis(500);
+ }
+ _engine->getCursor()->show(true);
+ }
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: playsbe <sbename> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a NIS animation sequence.
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlayNis(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String name(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ // If we got a nis filename, check that the file exists
+ if (name.contains('.') && _engine->getResourceManager()->hasFile(name)) {
+ DebugPrintf("Cannot find file: %s\n", name.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlayNis);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ // Make sure we are not called in a loop
+ _numParams = 0;
+
+
+ // Check if we got a nis filename or an animation index
+ if (name.contains('.')) {
+ Animation animation;
+ if (animation.load(getArchive(name))) {
+ _engine->getCursor()->show(false);
+ animation.play();
+ _engine->getCursor()->show(true);
+ }
+ } else {
+ getAction()->playAnimation((EventIndex)atoi(name.c_str()), true);
+ }
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: playnis <nisname.nis or animation index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: loads a scene
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdLoadScene(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ int cd = 1;
+ SceneIndex index = (SceneIndex)getNumber(argv[1]);
+
+ // Check args
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (index > 2500) {
+ DebugPrintf("Error: invalid index value (0-2500)");
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdLoadScene);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ /************ DEBUG *************************/
+ // Use to find scenes with certain values
+
+ //for (int i = index; i < 2500; i++) {
+ // loadSceneObject(scene, i);
+
+ // if (scene.getHeader() && scene.getHeader()->car == 5 && scene.getHeader()->position == 81) {
+ // DebugPrintf("Found scene: %d", i);
+
+ // // Draw scene found
+ // _engine->getGraphicsManager()->draw(&scene, GraphicsManager::kBackgroundC);
+
+ // askForRedraw();
+ // redrawScreen();
+ // _engine->_system->delayMillis(500);
+
+ // break;
+ // }
+ //}
+
+ //delete _sceneLoader;
+ //resetCommand();
+ //return true;
+
+ /*********************************************/
+ Scene *scene = getScenes()->get(index);
+ if (!scene) {
+ DebugPrintf("Cannot load scene %i from CD %i", index, cd);
+ resetCommand();
+
+ return true;
+ }
+
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Pause for a second to be able to see the scene
+ _engine->_system->delayMillis(500);
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: loadscene <scene index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: starts a fight sequence
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdFight(int argc, const char **argv) {
+ if (argc == 2) {
+ FightType type = (FightType)getNumber(argv[1]);
+
+ // Load proper data file
+ ArchiveIndex index = kArchiveCd1;
+ switch (type) {
+ default:
+ goto error;
+
+ case kFightMilos:
+ index = kArchiveCd1;
+ break;
+
+ case kFightAnna:
+ index = kArchiveCd2;
+ break;
+
+ case kFightIvo:
+ case kFightSalko:
+ case kFightVesna:
+ index = kArchiveCd3;
+ break;
+ }
+
+ loadArchive(index);
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdFight);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ // Make sure we are not called in a loop
+ _numParams = 0;
+
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+
+ SceneIndex lastScene = getState()->scene;
+
+ getFight()->setup(type) ? DebugPrintf("Lost fight!\n") : DebugPrintf("Won fight!\n");
+
+ // Pause for a second to be able to see the final scene
+ _engine->_system->delayMillis(1000);
+
+ // Restore loaded archive
+ restoreArchive();
+
+ // Stop audio and restore scene
+ getSound()->stopAllSound();
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ Scene *scene = getScenes()->get(lastScene);
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ resetCommand();
+ }
+ } else {
+error:
+ DebugPrintf("Syntax: fight <id> (id=2001-2005)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: starts the beetle sequence
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdBeetle(int argc, const char **argv) {
+ if (argc == 1) {
+ // Load proper data file (beetle game in in Cd2)
+ loadArchive(kArchiveCd2);
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdBeetle);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+
+ // Save current state
+ SceneIndex previousScene = getState()->scene;
+ ObjectLocation previousLocation = getInventory()->get(kItemBeetle)->location;
+ ChapterIndex previousChapter = (ChapterIndex)getProgress().chapter;
+
+ // Setup scene & inventory
+ getProgress().chapter = kChapter2;
+ Scene *scene = getScenes()->get(kSceneBeetle);
+ getInventory()->get(kItemBeetle)->location = kObjectLocation3;
+
+ askForRedraw();
+ redrawScreen();
+
+ // Load the beetle game
+ Action *action = NULL;
+ Beetle *beetle = new Beetle(_engine);
+ if (!beetle->isLoaded())
+ beetle->load();
+
+ // Play the game
+ Common::Event ev;
+ bool playgame = true;
+ while (playgame) {
+ // Update beetle
+ beetle->update();
+
+ askForRedraw();
+ redrawScreen();
+
+ while (g_engine->getEventManager()->pollEvent(ev)) {
+
+ switch (ev.type) {
+ default:
+ break;
+
+ case Common::EVENT_KEYDOWN:
+ // Exit beetle game on escape
+ if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+ playgame = false;
+
+ break;
+
+ case Common::EVENT_MOUSEMOVE: {
+ // Update cursor
+ CursorStyle style = kCursorNormal;
+ SceneHotspot *hotspot = NULL;
+ if (scene->checkHotSpot(ev.mouse, &hotspot)) {
+ if (!action)
+ action = new Action(_engine);
+
+ style = action->getCursor(*hotspot);
+ }
+
+ _engine->getCursor()->setStyle(style);
+ break;
+ }
+
+
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ // Update coordinates
+ getLogic()->getGameState()->setCoordinates(ev.mouse);
+
+ if (beetle->catchBeetle())
+ playgame = false;
+ break;
+ }
+
+ _engine->_system->delayMillis(10);
+ }
+ }
+
+ // Cleanup
+ beetle->unload();
+ delete beetle;
+ delete action;
+
+ // Pause for a second to be able to see the final scene
+ _engine->_system->delayMillis(1000);
+
+ // Restore state
+ getProgress().chapter = previousChapter;
+ getInventory()->get(kItemBeetle)->location = previousLocation;
+
+ // Restore loaded archive
+ restoreArchive();
+
+ // Stop audio and restore scene
+ getSound()->stopAllSound();
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ Scene *oldScene = getScenes()->get(previousScene);
+ _engine->getGraphicsManager()->draw(oldScene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: beetle\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: adjusts the time delta
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdTimeDelta(int argc, const char **argv) {
+ if (argc == 2) {
+ int delta = getNumber(argv[1]);
+
+ if (delta <= 0 || delta > 500)
+ goto label_error;
+
+ getState()->timeDelta = (uint)delta;
+ } else {
+label_error:
+ DebugPrintf("Syntax: delta <time delta> (delta=1-500)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Convert between in-game time and human readable time
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdTime(int argc, const char **argv) {
+ if (argc == 2) {
+ int32 time = getNumber(argv[1]);
+
+ if (time < 0)
+ goto label_error;
+
+ // Convert to human-readable form
+ uint8 hours = 0;
+ uint8 minutes = 0;
+ State::getHourMinutes((uint32)time, &hours, &minutes);
+
+ DebugPrintf("%02d:%02d\n", hours, minutes);
+ } else {
+label_error:
+ DebugPrintf("Syntax: time <time to convert> (time=0-INT_MAX)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: show game logic data
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShow(int argc, const char **argv) {
+#define OUTPUT_DUMP(name, text) \
+ DebugPrintf(#name "\n"); \
+ DebugPrintf("--------------------------------------------------------------------\n\n"); \
+ DebugPrintf("%s", text); \
+ DebugPrintf("\n");
+
+ if (argc == 2) {
+
+ Common::String name(const_cast<char *>(argv[1]));
+
+ if (name == "state" || name == "st") {
+ OUTPUT_DUMP("Game state", getState()->toString().c_str());
+ } else if (name == "progress" || name == "pr") {
+ OUTPUT_DUMP("Progress", getProgress().toString().c_str());
+ } else if (name == "flags" || name == "fl") {
+ OUTPUT_DUMP("Flags", getFlags()->toString().c_str());
+ } else if (name == "inventory" || name == "inv") {
+ OUTPUT_DUMP("Inventory", getInventory()->toString().c_str());
+ } else if (name == "objects" || name == "obj") {
+ OUTPUT_DUMP("Objects", getObjects()->toString().c_str());
+ } else if (name == "savepoints" || name == "pt") {
+ OUTPUT_DUMP("SavePoints", getSavePoints()->toString().c_str());
+ } else if (name == "scene" || name == "sc") {
+ OUTPUT_DUMP("Current scene", getScenes()->get(getState()->scene)->toString().c_str());
+ } else {
+ goto label_error;
+ }
+
+ } else {
+label_error:
+ DebugPrintf("Syntax: state <option>\n");
+ DebugPrintf(" state / st\n");
+ DebugPrintf(" progress / pr\n");
+ DebugPrintf(" flags / fl\n");
+ DebugPrintf(" inventory / inv\n");
+ DebugPrintf(" objects / obj\n");
+ DebugPrintf(" savepoints / pt\n");
+ DebugPrintf(" scene / sc\n");
+ }
+
+ return true;
+
+#undef OUTPUT_DUMP
+}
+
+/**
+ * Command: shows entity data
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdEntity(int argc, const char **argv) {
+ if (argc == 2) {
+ EntityIndex index = (EntityIndex)getNumber(argv[1]);
+
+ if (index > 39)
+ goto label_error;
+
+ DebugPrintf("Entity %s\n", ENTITY_NAME(index));
+ DebugPrintf("--------------------------------------------------------------------\n\n");
+ DebugPrintf("%s", getEntities()->getData(index)->toString().c_str());
+
+ // The Player entity does not have any callback data
+ if (index != kEntityPlayer) {
+ EntityData *data = getEntities()->get(index)->getParamData();
+ for (uint callback = 0; callback < 9; callback++) {
+ DebugPrintf("Call parameters %d:\n", callback);
+ for (byte ix = 0; ix < 4; ix++)
+ DebugPrintf(" %s", data->getParameters(callback, ix)->toString().c_str());
+ }
+ }
+
+ DebugPrintf("\n");
+ } else {
+label_error:
+ DebugPrintf("Syntax: entity <index>\n");
+ for (int i = 0; i < 40; i += 4)
+ DebugPrintf(" %s - %d %s - %d %s - %d %s - %d\n", ENTITY_NAME(i), i, ENTITY_NAME(i+1), i+1, ENTITY_NAME(i+2), i+2, ENTITY_NAME(i+3), i+3);
+ }
+
+ return true;
+}
+
+/**
+ * Command: loads a game
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdLoadGame(int argc, const char **argv) {
+ if (argc == 2) {
+ int id = getNumber(argv[1]);
+
+ if (id == 0 || id > 6)
+ goto error;
+
+ getSaveLoad()->loadGame((GameId)(id - 1));
+ } else {
+error:
+ DebugPrintf("Syntax: loadgame <id> (id=1-6)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: switch to a specific chapter
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdSwitchChapter(int argc, const char **argv) {
+ if (argc == 2) {
+ int id = getNumber(argv[1]);
+
+ if (id <= 1 || id > 6)
+ goto error;
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdSwitchChapter);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ // Sets the current chapter and then call Logic::switchChapter to proceed to the next chapter
+ getState()->progress.chapter = (ChapterIndex)(id - 1);
+
+ getLogic()->switchChapter();
+
+ resetCommand();
+ }
+ } else {
+error:
+ DebugPrintf("Syntax: chapter <id> (id=2-6)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: clears the screen
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdClear(int argc, const char **) {
+ if (argc == 1) {
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+ } else {
+ DebugPrintf("Syntax: clear - clear the screen\n");
+ }
+
+ return true;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/debug.h b/engines/lastexpress/debug.h
new file mode 100644
index 0000000000..e935b35fc0
--- /dev/null
+++ b/engines/lastexpress/debug.h
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_DEBUG_H
+#define LASTEXPRESS_DEBUG_H
+
+#include "gui/debugger.h"
+
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/shared.h"
+
+namespace LastExpress {
+
+enum {
+ kLastExpressDebugAll = 1 << 0,
+ kLastExpressDebugGraphics = 1 << 1,
+ kLastExpressDebugResource = 1 << 2,
+ kLastExpressDebugCursor = 1 << 3,
+ kLastExpressDebugSound = 1 << 4,
+ kLastExpressDebugSubtitle = 1 << 5,
+ kLastExpressDebugSavegame = 1 << 6,
+ kLastExpressDebugLogic = 1 << 7,
+ kLastExpressDebugScenes = 1 << 8,
+ kLastExpressDebugUnknown = 1 << 9
+ // the current limitation is 32 debug levels (1 << 31 is the last one)
+};
+
+class LastExpressEngine;
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger(LastExpressEngine *engine);
+ ~Debugger();
+
+ bool hasCommand() const;
+ void callCommand();
+
+private:
+ LastExpressEngine *_engine;
+
+ bool cmdHelp(int argc, const char **argv);
+
+ bool cmdListFiles(int argc, const char **argv);
+ bool cmdDumpFiles(int argc, const char **argv);
+
+ bool cmdShowFrame(int argc, const char **argv);
+ bool cmdShowBg(int argc, const char **argv);
+ bool cmdPlaySeq(int argc, const char **argv);
+ bool cmdPlaySnd(int argc, const char **argv);
+ bool cmdPlaySbe(int argc, const char **argv);
+ bool cmdPlayNis(int argc, const char **argv);
+
+ bool cmdLoadScene(int argc, const char **argv);
+ bool cmdFight(int argc, const char **argv);
+ bool cmdBeetle(int argc, const char **argv);
+
+ bool cmdTimeDelta(int argc, const char **argv);
+ bool cmdTime(int argc, const char **argv);
+ bool cmdShow(int argc, const char **argv);
+ bool cmdEntity(int argc, const char **argv);
+
+ bool cmdLoadGame(int argc, const char **argv);
+ bool cmdSwitchChapter(int argc, const char **argv);
+ bool cmdClear(int argc, const char **argv);
+
+ void resetCommand();
+ void copyCommand(int argc, const char **argv);
+ int getNumber(const char *arg) const;
+
+ void loadArchive(ArchiveIndex index) const;
+ void restoreArchive() const;
+
+ Debuglet *_command;
+ int _numParams;
+ char **_commandParams;
+
+ // Special sound stream for playing sounds
+ StreamedSound *_soundStream;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_DEBUG_H
diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp
new file mode 100644
index 0000000000..d72a5eab86
--- /dev/null
+++ b/engines/lastexpress/detection.cpp
@@ -0,0 +1,211 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+static const PlainGameDescriptor lastExpressGames[] = {
+ // Games
+ {"lastexpress", "The Last Express"},
+ {0, 0}
+};
+
+static const ADGameDescription gameDescriptions[] = {
+
+ // The Last Express (English) - US Broderbund Release
+ // expressw.exe 1997-02-12 17:24:44
+ // express.exe 1997-02-12 17:29:08
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "2d331459e0e68cf277ef4e4043750413", 29865984}, // 1997-02-10 19:38:19
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944}, // 1997-02-10 17:04:40
+ {"CD2.HPF", 0, "58aa26e782d10ec5d2231e539d2fe6a2", 669581312}, // 1997-02-10 16:19:30
+ {"CD3.HPF", 0, "00554fbf78a2ad391d98578fbbbe1c48", 641128448}, // 1997-02-10 15:44:09
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (English) - UK Broderbund Release
+ // expressw.exe 1997-04-02 14:30:32
+ // express.exe 1997-04-02 15:00:50
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "2d331459e0e68cf277ef4e4043750413", 29865984}, // 1997-04-10 11:03:41
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944}, // 1997-04-10 11:03:36
+ {"CD2.HPF", 0, "2672348691e1ae22d37d9f46f3683a07", 669509632}, // 1997-04-11 09:48:33
+ {"CD3.HPF", 0, "33f5e35f51063cb90f6bed9974475aa6", 641056768}, // 1997-04-11 09:48:55
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (English) - Interplay Release
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "bcc32d977f92bb52c060a0b4e8589cac", 30715904},
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944},
+ {"CD2.HPF", 0, "58aa26e782d10ec5d2231e539d2fe6a2", 669581312},
+ {"CD3.HPF", 0, "00554fbf78a2ad391d98578fbbbe1c48", 641128448},
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (French) - Broderbund Release
+ // expressw.exe 1997-04-02 09:31:24
+ // express.exe 1997-04-02 10:01:12
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "c14c6d685d9bf8705d9f659062e6c5c2", 29505536}, // 1997-04-03 07:53:20
+ {"CD1.HPF", 0, "b4277b22bc5cd6ad3b00c2ec04d4645d", 522924032}, // 1997-04-03 07:53:14
+ {"CD2.HPF", 0, "8c9610aa4cb707ab51f61c30feb22c1a", 665710592}, // 1997-04-09 12:04:30
+ {"CD3.HPF", 0, "411c1bab57b3e8da4fb359c5b40ef5d7", 640884736}, // 1997-04-03 08:21:47
+ },
+ Common::FR_FRA,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (German)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "7cdd70fc0b1555785f1e9e8d371ea85c", 31301632},
+ {"CD1.HPF", 0, "6d74cc861d172466bc745ff8bf0e59c5", 522971136},
+ {"CD2.HPF", 0, "b71ac9391de415807c74ff078f4fab22", 655702016},
+ {"CD3.HPF", 0, "ee55d4310546dd2a38560b096d1c2771", 641144832},
+ },
+ Common::DE_DEU,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (Spanish)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "46bed8832f06cf7160883a2aae2d667f", 29657088},
+ {"CD1.HPF", 0, "367a3a8581f6f88ddc51af7cde105ba9", 519927808},
+ {"CD2.HPF", 0, "af5566df3000472852ec182c9ec57797", 662210560},
+ {"CD3.HPF", 0, "0d1901662f4d063a5c250c9fbf64b771", 639504384},
+ },
+ Common::ES_ESP,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (Demo) - Broderbund
+ // expressw.exe 1997-08-14 14:09:42
+ // express.exe 1997-08-14 14:19:34
+ {
+ "lastexpress",
+ "Demo",
+ {
+ {"Demo.HPF", 0, "baf3b1f64155d34872896e61c3d3cb78", 58191872}, // 1997-08-14 14:44:26
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ AD_TABLE_END_MARKER
+};
+
+static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)gameDescriptions,
+ // Size of that superset structure
+ sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ lastExpressGames,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ "lastexpress",
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0,
+ // Additional GUI options (for every game}
+ Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
+};
+
+
+class LastExpressMetaEngine : public AdvancedMetaEngine {
+public:
+ LastExpressMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ const char *getName() const {
+ return "LastExpress Engine";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "LastExpress Engine (C) 1997 Smoking Car Productions";
+ }
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+};
+
+bool LastExpressMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (gd) {
+ *engine = new LastExpressEngine(syst, (const ADGameDescription *)gd);
+ }
+ return gd != 0;
+}
+
+} // End of namespace LastExpress
+
+#if PLUGIN_ENABLED_DYNAMIC(LASTEXPRESS)
+ REGISTER_PLUGIN_DYNAMIC(LASTEXPRESS, PLUGIN_TYPE_ENGINE, LastExpress::LastExpressMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(LASTEXPRESS, PLUGIN_TYPE_ENGINE, LastExpress::LastExpressMetaEngine);
+#endif
diff --git a/engines/lastexpress/drawable.h b/engines/lastexpress/drawable.h
new file mode 100644
index 0000000000..e273876362
--- /dev/null
+++ b/engines/lastexpress/drawable.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_DRAWABLE_H
+#define LASTEXPRESS_DRAWABLE_H
+
+#include "graphics/surface.h"
+
+namespace LastExpress {
+
+class Drawable {
+public:
+ virtual ~Drawable() {}
+
+ virtual Common::Rect draw(Graphics::Surface *surface) = 0;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_DRAWABLE_H
diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp
new file mode 100644
index 0000000000..777767600f
--- /dev/null
+++ b/engines/lastexpress/entities/abbot.cpp
@@ -0,0 +1,1910 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/abbot.h"
+
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Abbot::Abbot(LastExpressEngine *engine) : Entity(engine, kEntityAbbot) {
+ ADD_CALLBACK_FUNCTION(Abbot, reset);
+ ADD_CALLBACK_FUNCTION(Abbot, draw);
+ ADD_CALLBACK_FUNCTION(Abbot, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Abbot, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Abbot, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Abbot, draw2);
+ ADD_CALLBACK_FUNCTION(Abbot, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Abbot, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Abbot, playSound);
+ ADD_CALLBACK_FUNCTION(Abbot, savegame);
+ ADD_CALLBACK_FUNCTION(Abbot, updateEntity);
+ ADD_CALLBACK_FUNCTION(Abbot, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Abbot, updatePosition);
+ ADD_CALLBACK_FUNCTION(Abbot, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter1);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter2);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter3);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function19);
+ ADD_CALLBACK_FUNCTION(Abbot, function20);
+ ADD_CALLBACK_FUNCTION(Abbot, function21);
+ ADD_CALLBACK_FUNCTION(Abbot, function22);
+ ADD_CALLBACK_FUNCTION(Abbot, function23);
+ ADD_CALLBACK_FUNCTION(Abbot, function24);
+ ADD_CALLBACK_FUNCTION(Abbot, function25);
+ ADD_CALLBACK_FUNCTION(Abbot, function26);
+ ADD_CALLBACK_FUNCTION(Abbot, function27);
+ ADD_CALLBACK_FUNCTION(Abbot, function28);
+ ADD_CALLBACK_FUNCTION(Abbot, function29);
+ ADD_CALLBACK_FUNCTION(Abbot, function30);
+ ADD_CALLBACK_FUNCTION(Abbot, function31);
+ ADD_CALLBACK_FUNCTION(Abbot, function32);
+ ADD_CALLBACK_FUNCTION(Abbot, function33);
+ ADD_CALLBACK_FUNCTION(Abbot, function34);
+ ADD_CALLBACK_FUNCTION(Abbot, function35);
+ ADD_CALLBACK_FUNCTION(Abbot, function36);
+ ADD_CALLBACK_FUNCTION(Abbot, function37);
+ ADD_CALLBACK_FUNCTION(Abbot, function38);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter4);
+ ADD_CALLBACK_FUNCTION(Abbot, function40);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function42);
+ ADD_CALLBACK_FUNCTION(Abbot, function43);
+ ADD_CALLBACK_FUNCTION(Abbot, function44);
+ ADD_CALLBACK_FUNCTION(Abbot, function45);
+ ADD_CALLBACK_FUNCTION(Abbot, function46);
+ ADD_CALLBACK_FUNCTION(Abbot, drinkAfterDefuse);
+ ADD_CALLBACK_FUNCTION(Abbot, function48);
+ ADD_CALLBACK_FUNCTION(Abbot, pickBomb);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter5);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function52);
+ ADD_CALLBACK_FUNCTION(Abbot, function53);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Abbot, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Abbot, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Abbot, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Abbot, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarRedSleeping, kObjectCompartmentC, true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Abbot, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(6, Abbot, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(7, Abbot, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(8, Abbot, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(9, Abbot, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Abbot, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(11, Abbot, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 18) || getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ getSound()->excuseMe(kEntityAbbot);
+ } else {
+ if (getEvent(kEventAbbotIntroduction))
+ getSound()->playSound(kEntityPlayer, "CAT1013");
+ else
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(12, Abbot, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(13, Abbot, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Abbot, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Abbot, chapter1)
+ if (savepoint.action == kActionDefault)
+ getSavePoints()->addData(kEntityAbbot, kAction203073664, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Abbot, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityAbbot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Abbot, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ getData()->clothes = kClothesDefault;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("804DD");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAbbot, kEntityCooks, kAction236976550);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(5);
+ setup_enterExitCompartment("617AC", kObjectCompartmentC);
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction192054567:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Abbot, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1953000) {
+ if (!params->param1) {
+ params->param1 = 1;
+ setCallback(3);
+ setup_playSound("MrB3010");
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setCallback(1);
+ setup_playSound("Abb3010");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateFromTime(900);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ setup_function20();
+ break;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Abbot, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1966500 && getEntities()->isInRestaurant(kEntityBoutarel))
+ setup_function21();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "509A");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Abbot, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("509B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Mc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("804US");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceRight(kEntityAbbot, "029J");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029H");
+ getSavePoints()->push(kEntityAbbot, kEntityPascale, kAction207769280);
+ break;
+
+ case 7:
+ setup_function22();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getSavePoints()->push(kEntityAbbot, kEntityTables4, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(7);
+ setup_draw("029B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Abbot, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752);
+
+ if (getState()->time > kTime1989000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function23();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotIntroduction);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ if (!getEvent(kEventAbbotIntroduction))
+ getData()->inventoryItem = (InventoryItem)kCursorProcess;
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getAction()->playAnimation(kEventAbbotIntroduction);
+ getSound()->playSound(kEntityPlayer, "LIB036");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Abbot, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_callSavepoint("029F", kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction238936000);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Abbot, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 900);
+
+ setup_function25();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.action == kActionKnock) {
+ setCallback(1);
+ setup_playSound("LIB012");
+ } else {
+ setCallback(2);
+ setup_playSound("LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("Abb3001");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Abbot, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("617Dc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Abbot, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon())
+ setup_function27();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityKronos, kAction157159392);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ break;
+
+ case kAction101169422:
+ params->param1 = 1;
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Abbot, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("115C", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Abbot, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2052000, params->param1, 1, setup_function29);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+
+ setCallback(1);
+ setup_playSound("abb3013");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case kAction222609266:
+ setup_function30();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Abbot, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTicks(450);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(225);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(7);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Abbot, function30)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("Abb3030");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function31();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Abbot, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+ if (getState()->time < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 450;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+ }
+ }
+
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param5, getState()->time, 450);
+
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDefault:
+ params->param2 = (uint)getState()->time + 4500;
+ params->param3 = (uint)getState()->time + 18000;
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("115E", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122358304);
+ getSound()->playSound(kEntityAbbot, "Abb3020");
+
+ setCallback(3);
+ setup_updatePosition("125A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "125B");
+
+ setCallback(4);
+ setup_playSound("Abb3021");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAbbot, "Abb3023");
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 52);
+
+ setCallback(5);
+ setup_draw2("125C1", "125C2", kEntityAlexei);
+ break;
+
+ case 5:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 52);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "125D");
+ getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122288808);
+ params->param1 = 1;
+
+ UPDATE_PARAM(params->param5, getState()->time, 450);
+
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updatePosition("125E", kCarRestaurant, 52);
+ break;
+
+ case 7:
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Abbot, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setup_function33();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Abbot, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid && getState()->time > kTime2115000) {
+ if (getState()->time <= kTime2124000) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 2000) || !params->param1)
+ params->param1 = (uint)getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb3014");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case kAction123712592:
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Abbot, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("Abb3031");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ getData()->location = kLocationInsideCompartment;
+ setup_function35();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Abbot, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param2)
+ params->param2 = (uint)getState()->time + 450;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityAbbot, kEntityAugust, kAction136196244);
+
+ setCallback(1);
+ setup_updateFromTime(0);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ params->param1 = (uint)getState()->time + 9000;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityAbbot, "Abb3040", SoundManager::kFlagInvalid, 45);
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 57);
+
+ setCallback(3);
+ setup_callSavepoint("121A", kEntityAugust, kAction122358304, "BOGUS");
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 57);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function36();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Abbot, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ switch (params->param2) {
+ default:
+ break;
+
+ case 1:
+ if (params->param3 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time + 675;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ getSound()->playSound(kEntityAbbot, "Abb3041");
+ break;
+
+ case 2:
+ UPDATE_PARAM(params->param4, getState()->time, 900);
+
+ getSound()->playSound(kEntityAbbot, "Abb3042");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAbbot, "Abb3043");
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 57);
+
+ setCallback(1);
+ setup_callSavepoint("121D", kEntityAugust, kAction122288808, "BOGUS");
+ break;
+ }
+ break;
+
+ case kActionEndSound:
+ ++params->param2;
+ break;
+
+ case kActionDefault:
+ params->param1 = (uint)getState()->time + 4500;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "121B");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 57);
+ setup_function37();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Abbot, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Abbot, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+
+ setCallback(1);
+ setup_playSound("Abb3014A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Abbot, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (!getEvent(kEventAbbotInvitationDrink)
+ && getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotInvitationDrink);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAbbotInvitationDrink);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Abbot, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129);
+
+ if (getState()->time > kTime2389500 && getEntities()->isSomebodyInsideRestaurantOrSalon())
+ setup_function42();
+
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityTables4, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Abbot, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_callSavepoint("029F", kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Abbot, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+ if (getState()->time < kTime2452500) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb4002");
+ break;
+ } else {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000) || getSound()->isBuffered(kEntityBoutarel) || !params->param4)
+ params->param4 = (uint)getState()->time + 450;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb4002");
+ break;
+ }
+ }
+ }
+
+label_callback_1:
+ TIME_CHECK(kTime2466000, params->param5, setup_function44);
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 75);
+
+ params->param2 = 1;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param6 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param3) {
+ setCallback(savepoint.param.intValue == 50 ? 5 : 6);
+ setup_playSound(savepoint.param.intValue == 50 ? getSound()->justAMinuteCath() : getSound()->wrongDoorCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_playSound("Abb3001");
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorTalk, kCursorNormal);
+
+ params->param3 = 1;
+ break;
+
+ case 5:
+ case 6:
+ params->param2 = 1;
+ params->param3 = 0;
+ break;
+ }
+ break;
+
+ case kAction101687594:
+ params->param1 = 1;
+ break;
+
+ case kAction159003408:
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Abbot, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction104060776:
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Abbot, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6471;
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationOutsideCompartment;
+
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function38);
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "617Ec");
+ getEntities()->enterCompartment(kEntityAbbot, kObjectCompartmentC, true);
+
+ setCallback(1);
+ setup_playSound("Abb4010");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("617Kc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAbbot, kObjectCompartmentC, true);
+ getSavePoints()->push(kEntityAbbot, kEntityVerges, kAction125233040);
+
+ setup_function46();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Abbot, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6471;
+
+ setCallback(1);
+ setup_function40(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_drinkAfterDefuse();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Abbot, drinkAfterDefuse)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotDrinkGiveDetonator);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("126A");
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ getData()->inventoryItem = kItemBomb;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAbbotDrinkGiveDetonator);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Abbot, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1))
+ getData()->inventoryItem = kItemInvalid;
+
+ UPDATE_PARAM_PROC(params->param1, getState()->time, 1800)
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_updatePosition("126C", kCarRedSleeping, 52);
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotDrinkDefuse);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ getSavePoints()->push(kEntityAbbot, kEntityVerges, kAction125233040);
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("126A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ break;
+
+ case 4:
+ if (!getEvent(kEventAbbotDrinkDefuse) && ENTITY_PARAM(0, 1))
+ getData()->inventoryItem = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ params->param1 = 0;
+
+ TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_updatePosition("126D", kCarRestaurant, 52);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+
+ setup_function44();
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventAbbotDrinkDefuse);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction100969180);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 58);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Abbot, pickBomb)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 150);
+
+ getSavePoints()->push(kEntityAbbot, kEntityAbbot, kAction157489665);
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ case kAction157489665:
+ getSavePoints()->push(kEntityAbbot, kEntityTatiana, kAction238790488);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSavePoints()->call(kEntityAbbot, kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction125039808);
+ getObjects()->update(kObjectCompartment2, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(getObjects()->get(kObjectCompartment2).location2 < kObjectLocation2 ? kEventAbbotWrongCompartmentBed : kEventAbbotWrongCompartment);
+ getEntities()->updateEntity(kEntityAbbot, kCarRedSleeping, kPosition_6470);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromObject(kObjectCompartment2, true);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Abbot, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Abbot, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function52();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Abbot, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+ break;
+
+ case kAction135600432:
+ setup_function53();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Abbot, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getInventory()->setLocationAndProcess(kItem25, kObjectLocation1);
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction158480160);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventLocomotiveAbbotGetSomeRest);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventLocomotiveAbbotShoveling);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+
+ case kAction168646401:
+ if (!getEvent(kEventLocomotiveAbbotGetSomeRest)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAbbotGetSomeRest);
+ break;
+ }
+
+ if (!getEvent(kEventLocomotiveAbbotShoveling)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAbbotShoveling);
+ break;
+ }
+
+ getAction()->playAnimation(kEventLocomotiveAbbotShoveling);
+ getScenes()->processScene();
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h
new file mode 100644
index 0000000000..7e7b78b3be
--- /dev/null
+++ b/engines/lastexpress/entities/abbot.h
@@ -0,0 +1,225 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ABBOT_H
+#define LASTEXPRESS_ABBOT_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Abbot : public Entity {
+public:
+ Abbot(LastExpressEngine *engine);
+ ~Abbot() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * ???
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function40, CarIndex car, EntityPosition position)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(function43)
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(drinkAfterDefuse)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(pickBomb)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ABBOT_H
diff --git a/engines/lastexpress/entities/alexei.cpp b/engines/lastexpress/entities/alexei.cpp
new file mode 100644
index 0000000000..59b99fe968
--- /dev/null
+++ b/engines/lastexpress/entities/alexei.cpp
@@ -0,0 +1,2002 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/alexei.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Alexei::Alexei(LastExpressEngine *engine) : Entity(engine, kEntityAlexei) {
+ ADD_CALLBACK_FUNCTION(Alexei, reset);
+ ADD_CALLBACK_FUNCTION(Alexei, playSound);
+ ADD_CALLBACK_FUNCTION(Alexei, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Alexei, draw);
+ ADD_CALLBACK_FUNCTION(Alexei, updatePosition);
+ ADD_CALLBACK_FUNCTION(Alexei, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Alexei, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Alexei, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Alexei, savegame);
+ ADD_CALLBACK_FUNCTION(Alexei, updateEntity);
+ ADD_CALLBACK_FUNCTION(Alexei, draw2);
+ ADD_CALLBACK_FUNCTION(Alexei, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Alexei, function13);
+ ADD_CALLBACK_FUNCTION(Alexei, function14);
+ ADD_CALLBACK_FUNCTION(Alexei, function15);
+ ADD_CALLBACK_FUNCTION(Alexei, function16);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter1);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function19);
+ ADD_CALLBACK_FUNCTION(Alexei, function20);
+ ADD_CALLBACK_FUNCTION(Alexei, function21);
+ ADD_CALLBACK_FUNCTION(Alexei, function22);
+ ADD_CALLBACK_FUNCTION(Alexei, function23);
+ ADD_CALLBACK_FUNCTION(Alexei, function24);
+ ADD_CALLBACK_FUNCTION(Alexei, function25);
+ ADD_CALLBACK_FUNCTION(Alexei, function26);
+ ADD_CALLBACK_FUNCTION(Alexei, function27);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter2);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function30);
+ ADD_CALLBACK_FUNCTION(Alexei, function31);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter3);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function34);
+ ADD_CALLBACK_FUNCTION(Alexei, function35);
+ ADD_CALLBACK_FUNCTION(Alexei, function36);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter4);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function39);
+ ADD_CALLBACK_FUNCTION(Alexei, function40);
+ ADD_CALLBACK_FUNCTION(Alexei, function41);
+ ADD_CALLBACK_FUNCTION(Alexei, function42);
+ ADD_CALLBACK_FUNCTION(Alexei, function43);
+ ADD_CALLBACK_FUNCTION(Alexei, function44);
+ ADD_CALLBACK_FUNCTION(Alexei, function45);
+ ADD_CALLBACK_FUNCTION(Alexei, function46);
+ ADD_CALLBACK_FUNCTION(Alexei, function47);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Alexei, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Alexei, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(3, Alexei, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Alexei, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(5, Alexei, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Alexei, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Alexei, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(8, Alexei, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Alexei, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Alexei, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 18) || getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ getSound()->excuseMe(kEntityAlexei);
+ } else {
+ if (getEvent(kEventAlexeiSalonVassili) || (getEvent(kEventTatianaAskMatchSpeakRussian) && getInventory()->hasItem(kItemPassengerList))) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1012" : "CAT1012A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ }
+ // Stop execution here
+ return;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(11, Alexei, draw2)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Alexei, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Alexei, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction302614416);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602DB");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment2, true);
+ }
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_7500;
+ getEntities()->clearSequences(kEntityAlexei);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction135664192:
+ setCallback(2);
+ setup_enterExitCompartment("602Eb", kObjectCompartment2);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Alexei, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("602Fb", kObjectCompartment2);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction302614416);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602DB");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+ }
+ break;
+
+ case kAction135664192:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Alexei, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("103D", kCarRestaurant, 52);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(60) + 90);
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("103C", kCarRestaurant, 52);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103E");
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(16, Alexei, function16, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param7 && params->param1 < getState()->time && !params->param8) {
+ params->param8 = 1;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75);
+
+ params->param5 = 0;
+ params->param6 = 1;
+
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ CURRENT_PARAM(1, 1) = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param5) {
+ if (savepoint.param.intValue == 18) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : "CAT1503");
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAlexei, (char*)&params->seq);
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param6 || params->param5) {
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param5 = 0;
+ params->param6 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("ALX1134A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param5 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param5 = 0;
+ params->param6 = 1;
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTicks(300);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("602Gb", kObjectCompartment2);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602Hb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+ break;
+
+ case 10:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_7500;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, (char *)&params->seq);
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param7 = 0;
+ break;
+ }
+ break;
+
+ case kAction124697504:
+ setCallback(10);
+ setup_enterExitCompartment("602Ib", kObjectCompartment2);
+ break;
+
+ case kAction221617184:
+ params->param7 = 1;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction100906246);
+
+ setCallback(7);
+ setup_playSound("CON1024");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Alexei, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler)
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Alexei, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ params->param2 = kItemNone;
+
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+ getInventory()->setLocationAndProcess(kItem17, kObjectLocation1);
+
+ setCallback(1);
+ setup_callSavepoint("005D", kEntityTables1, kActionDrawTablesWithChairs, "005E");
+ break;
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ } else {
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction1:
+ params->param2 = kItemNone;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiDiner);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "005B");
+
+ params->param2 = kItemInvalid;
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 63) ? 1 : 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ setup_function19();
+ break;
+
+ case 2:
+ getAction()->playAnimation(getProgress().jacket == kJacketGreen ? kEventAlexeiDiner : kEventAlexeiDinerOriginalJacket);
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kActionDrawTablesWithChairs, "005E");
+
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityAlexei);
+ getInventory()->get(kItem17)->location = kObjectLocation1;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 63);
+
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ break;
+
+ case kAction225182640:
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Alexei, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("811DS");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("811US");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_draw("933");
+ break;
+
+ case 6:
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+ getScenes()->loadSceneFromItemPosition(kItem17);
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+
+ setCallback(7);
+ setup_callSavepoint("005F", kEntityTables1, kActionDrawTablesWithChairs, "005G");
+ break;
+
+ case 7:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityServers1, kAction302996448);
+
+ setCallback(8);
+ setup_draw("934");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_draw("811DS");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function13();
+ break;
+
+ case 10:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(11);
+ setup_function16(kTime1098000, "411");
+ break;
+
+ case 11:
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Alexei, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function14();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Alexei, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1)
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updatePosition("103C", kCarRestaurant, 52);
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ params->param1 = 225 * (4 * rnd(3) + 4);
+
+ if (!getEvent(kEventAlexeiSalonPoem))
+ getData()->inventoryItem = kItemParchemin;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ setup_function22();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "103D");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 52);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 52);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Alexei, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, params->param2)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updatePosition("103D", kCarRestaurant, 52);
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+
+ if (params->param3 == kTimeInvalid || getState()->time <= kTime1111500)
+ break;
+
+ if (getState()->time > kTime1138500) {
+ params->param3 = kTimeInvalid;
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time;
+
+ if (params->param3 >= getState()->time)
+ break;
+
+ params->param3 = kTimeInvalid;
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ setup_function23();
+ break;
+
+ case kAction1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(4) + 8);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103E");
+ if (!getEvent(kEventAlexeiSalonPoem))
+ getData()->inventoryItem = kItemParchemin;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ setup_function21();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "103D");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 52);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 52);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Alexei, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->inventoryItem = (!getEntities()->isInRestaurant(kEntityAlexei) || getEvent(kEventAlexeiSalonPoem)) ? kItemNone : kItemParchemin;
+ break;
+
+ case kAction1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction124973510);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAlexeiSalonVassili);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103F");
+ getScenes()->processScene();
+
+ setup_function24();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+
+ getData()->inventoryItem = kItemNone;
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ break;
+ }
+ break;
+
+ case kAction157159392:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonVassili);
+ } else {
+ setup_function24();
+ }
+ break;
+
+ case kAction188784532:
+ setup_function24();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Alexei, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonCath);
+ break;
+
+ case kActionDefault:
+ if (getEvent(kEventAlexeiSalonVassili))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAlexeiSalonCath);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_9460;
+ getEntities()->clearSequences(kEntityAlexei);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ setup_function25();
+ break;
+
+ case 2:
+ setup_function25();
+ break;
+ }
+ break;
+
+ case kAction135854208:
+ getData()->inventoryItem = kItemNone;
+ setCallback(2);
+ setup_draw("103G");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Alexei, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(2);
+ setup_function16(kTime1179000, "411");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(kTime1323000, "412");
+ break;
+
+ case 3:
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Alexei, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1512000, params->param1, setup_function27)
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->car = kCarGreenSleeping;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 66);
+
+ getEntities()->clearSequences(kEntityAlexei);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Alexei, function27)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "412");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Alexei, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Alexei, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime1791000, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("811US");
+ break;
+
+ case 5:
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ setCallback(6);
+ setup_callSavepoint("018B", kEntityTables1, kAction136455232, "BOGUS");
+ break;
+
+ case 6:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction290869168);
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Alexei, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getData()->car = kCarRestaurant;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "018C");
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction156444784);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "018E");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getProgress().field_68 = 1;
+
+ setCallback(2);
+ setup_playSound("TAT2116");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAlexei, "TAt2116A");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ setCallback(3);
+ setup_callSavepoint("018F", kEntityTatiana, kAction123857088, "BOGUS");
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ setup_function31();
+ break;
+ }
+ break;
+
+ case kAction236053296:
+ getEntities()->drawSequenceRight(kEntityAlexei, "018D1");
+ getEntities()->drawSequenceRight(kEntityTatiana, "018D2");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ if (savepoint.param.intValue)
+ getScenes()->loadSceneFromPosition(kCarRestaurant, (Position)savepoint.param.intValue);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Alexei, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityAlexei, "811DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAlexei);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function13();
+ break;
+
+ case 2:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(3);
+ setup_function16(kTimeEnd, "411");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Alexei, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Alexei, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function34();
+ break;
+
+ case kAction122288808:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Alexei, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(1);
+ setup_function16(kTime2083500, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function35();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function13();
+ break;
+
+ case 7:
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 66);
+
+ setCallback(8);
+ setup_function16(kTime2124000, "NONE");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function14();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function36();
+ break;
+
+ case 10:
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(11);
+ setup_function16(kTime16451100, "411");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Alexei, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 2700)
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ UPDATE_PARAM_PROC_END
+ } else {
+ params->param2 = 0;
+ }
+
+ UPDATE_PARAM_PROC(params->param3, getState()->time, params->param1)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setCallback(3);
+ setup_function15();
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+
+label_callback_3:
+ UPDATE_PARAM(params->param4, getState()->time, 9000);
+
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDefault:
+ params->param1 = 15 * rnd(120);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(getCallback() + 1);
+ setup_updatePosition("124C", kCarRestaurant, 52);
+ break;
+
+ case 2:
+ case 5:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ params->param1 = 15 * rnd(120);
+ params->param3 = 0;
+ goto label_callback_3;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Alexei, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 || params->param2)
+ break;
+
+ UPDATE_PARAM(params->param4, getState()->timeTicks, params->param1);
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "124B");
+
+ params->param2 = 1;
+ params->param4 = 0;
+ break;
+
+ case kActionExitCompartment:
+ if (params->param2) {
+ getEntities()->drawSequenceLeft(kEntityAlexei, "124A");
+ params->param1 = 5 * (3 * rnd(15) + 15);
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(15) + 15);
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAlexei, kEntityAbbot, kAction222609266);
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "124A");
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ setCallback(4);
+ setup_function13();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "BLANK");
+ params->param3 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Alexei, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Alexei, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2354400, "411");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function39();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Alexei, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2)
+ break;
+
+ if (!params->param4) {
+ params->param3 = (uint)getState()->time + 4500;
+ params->param4 = (uint)getState()->time + 9000;
+ }
+
+ if (params->param5 != kTimeInvalid && params->param3 < getState()->time) {
+
+ if (params->param4 >= getState()->time) {
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time;
+
+ if (params->param5 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ setup_function40();
+ }
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param2 && !params->param2)
+ getEntities()->drawSequenceLeft(kEntityAlexei, "306F");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("602FB", kObjectCompartment2);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62)) {
+ if (params->param1) {
+ if (!params->param2)
+ break;
+ } else if (!params->param2) {
+ getEntities()->drawSequenceRight(kEntityAlexei, "306A");
+ break;
+ }
+
+ setup_function40();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+ break;
+ }
+ break;
+
+ case kAction123536024:
+ params->param2 = 1;
+ break;
+
+ case kAction123712592:
+ getEntities()->clearSequences(kEntityAlexei);
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Alexei, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityAlexei, "602Eb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment2);
+ }
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2);
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAlexei);
+
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Alexei, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(1);
+ setup_function16(kTime2403000, "411");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function42();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Alexei, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function14();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction191198209);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Alexei, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time < kTime1806300 && params->param2 < getState()->time) {
+ if (!params->param2)
+ params->param2 = (uint)getState()->time + params->param1;
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setCallback(1);
+ setup_function15();
+ break;
+ }
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2457000 && !params->param3) {
+ params->param3 = 1;
+
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(120) + 180);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 5 * (3 * rnd(120) + 180);
+ params->param2 = 0;
+ goto label_callback_1;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("124C", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ setup_function44();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Alexei, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2457000 && !params->param1) {
+ params->param1 = 1;
+
+ getEntities()->updatePositionExit(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityAlexei, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+
+ setup_function45();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_9460;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62)) {
+ setCallback(2);
+ setup_draw("306A");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "306F");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Alexei, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ if (getInventory()->hasItem(kItemBomb)) {
+ setup_function46();
+ } else {
+ setCallback(2);
+ setup_function16(kTimeEnd, "412");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Alexei, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime2493000) {
+
+ if (getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityAugust) || !params->param1)
+ params->param1 = (uint)getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ getScenes()->loadSceneFromItemPosition(kItem22);
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, getScenes()->get(getState()->scene)->position);
+ }
+
+ setCallback(4);
+ setup_function13();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2488500, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 4:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(5);
+ setup_function16(kTime2507400, "412");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("602Fb", kObjectCompartment2);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 7:
+ getEntities()->drawSequenceRight(kEntityAlexei, "602Eb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartmentB);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ }
+
+ setCallback(8);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 8:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartmentB);
+ getEntities()->clearSequences(kEntityAlexei);
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(9);
+ setup_playSound("TAT4167");
+ break;
+
+ case 9:
+ getSavePoints()->push(kEntityAlexei, kEntityChapters, kAction156435676);
+ setup_function47();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Alexei, function47)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Alexei, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityAlexei);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/alexei.h b/engines/lastexpress/entities/alexei.h
new file mode 100644
index 0000000000..420e6e87fc
--- /dev/null
+++ b/engines/lastexpress/entities/alexei.h
@@ -0,0 +1,213 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ALEXEI_H
+#define LASTEXPRESS_ALEXEI_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Alexei : public Entity {
+public:
+ Alexei(LastExpressEngine *engine);
+ ~Alexei() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The sequence to draw for the second entity
+ * - The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_NOSETUP(draw2)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * ???
+ *
+ * @param timeValue The time value
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_2(function16, TimeValue timeValue, const char *sequence)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+ DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(function43)
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ALEXEI_H
diff --git a/engines/lastexpress/entities/alouan.cpp b/engines/lastexpress/entities/alouan.cpp
new file mode 100644
index 0000000000..2867fa726b
--- /dev/null
+++ b/engines/lastexpress/entities/alouan.cpp
@@ -0,0 +1,504 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/alouan.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Alouan::Alouan(LastExpressEngine *engine) : Entity(engine, kEntityAlouan) {
+ ADD_CALLBACK_FUNCTION(Alouan, reset);
+ ADD_CALLBACK_FUNCTION(Alouan, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Alouan, playSound);
+ ADD_CALLBACK_FUNCTION(Alouan, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Alouan, updateEntity);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment6);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment8);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment6to8);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment8to6);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter1);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function12);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter2);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter3);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter4);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function19);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter5);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function22);
+ ADD_CALLBACK_FUNCTION(Alouan, function23);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Alouan, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Alouan, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Alouan, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Alouan, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Alouan, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Alouan, compartment6)
+ COMPARTMENT_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Cf", "621Df");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Alouan, compartment8)
+ COMPARTMENT_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Alouan, compartment6to8)
+ COMPARTMENT_FROM_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Alouan, compartment8to6)
+ COMPARTMENT_FROM_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Alouan, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Alouan, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+
+ TIME_CHECK_CALLBACK(kTime1096200, params->param1, 1, setup_compartment8to6);
+
+label_callback1:
+ if (getState()->time > kTime1162800 && !params->param2) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4070);
+ getData()->entityPosition = kPosition_4070;
+ }
+
+ if (getState()->time > kTime1179000 && !params->param3) {
+ params->param3 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+
+ setCallback(2);
+ setup_compartment6to8();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4840;
+ goto label_callback1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Alouan, function12)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAlouan);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Alouan, chapter2)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Alouan, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime1777500) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2)
+ params->param2 = (uint)getState()->time + 75;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(params->param1 ? 1 : 2);
+ if (params->param1)
+ setup_compartment8();
+ else
+ setup_compartment6();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ params->param1 = 1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 3:
+ params->param1 = 0;
+ setCallback(4);
+ setup_playSound("Har2011");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(900);
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityAlouan, kEntityFrancois, kAction190219584);
+ break;
+ }
+ break;
+
+ case kAction189489753:
+ setCallback(3);
+ setup_compartment8to6();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Alouan, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Alouan, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTimeCitySalzbourg, params->param1, 1, setup_compartment8to6);
+
+label_callback1:
+ if (params->param2 != kTimeInvalid && getState()->time > kTime1989000)
+ TIME_CHECK_CAR(kTime2119500, params->param5, 5, setup_compartment8);
+
+label_callback2:
+ TIME_CHECK_CALLBACK_1(kTime2052000, params->param3, 3, setup_playSound, "Har1005");
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime2133000, params->param4, 4, setup_compartment6to8);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime2151000)
+ TIME_CHECK_CAR(kTime2241000, params->param5, 5, setup_compartment8);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4840;
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Alouan, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Alouan, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid)
+ TIME_CHECK_CAR(kTime2443500, params->param1, 1, setup_compartment8);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2455200, params->param2, 2, setup_compartment8to6);
+
+label_callback2:
+ if (getState()->time > kTime2475000 && !params->param3) {
+ params->param3 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+
+ setCallback(3);
+ setup_compartment6to8();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4070);
+ goto label_callback2;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Alouan, function19)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAlouan);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Alouan, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Alouan, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function22();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Alouan, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ setup_function23();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Alouan, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("619AF", kObjectCompartment5);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment6, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(24, Alouan)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/alouan.h b/engines/lastexpress/entities/alouan.h
new file mode 100644
index 0000000000..33d5e2f23d
--- /dev/null
+++ b/engines/lastexpress/entities/alouan.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ALOUAN_H
+#define LASTEXPRESS_ALOUAN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Alouan : public Entity {
+public:
+ Alouan(LastExpressEngine *engine);
+ ~Alouan() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(compartment6)
+ DECLARE_FUNCTION(compartment8)
+ DECLARE_FUNCTION(compartment6to8)
+ DECLARE_FUNCTION(compartment8to6)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ALOUAN_H
diff --git a/engines/lastexpress/entities/anna.cpp b/engines/lastexpress/entities/anna.cpp
new file mode 100644
index 0000000000..e6752dad48
--- /dev/null
+++ b/engines/lastexpress/entities/anna.cpp
@@ -0,0 +1,4032 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/anna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) {
+ ADD_CALLBACK_FUNCTION(Anna, reset);
+ ADD_CALLBACK_FUNCTION(Anna, draw);
+ ADD_CALLBACK_FUNCTION(Anna, updatePosition);
+ ADD_CALLBACK_FUNCTION(Anna, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Anna, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Anna, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Anna, playSound);
+ ADD_CALLBACK_FUNCTION(Anna, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Anna, savegame);
+ ADD_CALLBACK_FUNCTION(Anna, updateEntity);
+ ADD_CALLBACK_FUNCTION(Anna, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Anna, function12);
+ ADD_CALLBACK_FUNCTION(Anna, draw2);
+ ADD_CALLBACK_FUNCTION(Anna, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Anna, function15);
+ ADD_CALLBACK_FUNCTION(Anna, chapter1);
+ ADD_CALLBACK_FUNCTION(Anna, function17);
+ ADD_CALLBACK_FUNCTION(Anna, function18);
+ ADD_CALLBACK_FUNCTION(Anna, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function20);
+ ADD_CALLBACK_FUNCTION(Anna, function21);
+ ADD_CALLBACK_FUNCTION(Anna, function22);
+ ADD_CALLBACK_FUNCTION(Anna, function23);
+ ADD_CALLBACK_FUNCTION(Anna, function24);
+ ADD_CALLBACK_FUNCTION(Anna, function25);
+ ADD_CALLBACK_FUNCTION(Anna, function26);
+ ADD_CALLBACK_FUNCTION(Anna, function27);
+ ADD_CALLBACK_FUNCTION(Anna, function28);
+ ADD_CALLBACK_FUNCTION(Anna, function29);
+ ADD_CALLBACK_FUNCTION(Anna, function30);
+ ADD_CALLBACK_FUNCTION(Anna, function31);
+ ADD_CALLBACK_FUNCTION(Anna, function32);
+ ADD_CALLBACK_FUNCTION(Anna, function33);
+ ADD_CALLBACK_FUNCTION(Anna, function34);
+ ADD_CALLBACK_FUNCTION(Anna, function35);
+ ADD_CALLBACK_FUNCTION(Anna, function36);
+ ADD_CALLBACK_FUNCTION(Anna, function37);
+ ADD_CALLBACK_FUNCTION(Anna, function38);
+ ADD_CALLBACK_FUNCTION(Anna, function39);
+ ADD_CALLBACK_FUNCTION(Anna, function40);
+ ADD_CALLBACK_FUNCTION(Anna, function41);
+ ADD_CALLBACK_FUNCTION(Anna, chapter2);
+ ADD_CALLBACK_FUNCTION(Anna, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Anna, chapter3);
+ ADD_CALLBACK_FUNCTION(Anna, function45);
+ ADD_CALLBACK_FUNCTION(Anna, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function47);
+ ADD_CALLBACK_FUNCTION(Anna, function48);
+ ADD_CALLBACK_FUNCTION(Anna, leaveTableWithAugust);
+ ADD_CALLBACK_FUNCTION(Anna, function50);
+ ADD_CALLBACK_FUNCTION(Anna, function51);
+ ADD_CALLBACK_FUNCTION(Anna, function52);
+ ADD_CALLBACK_FUNCTION(Anna, function53);
+ ADD_CALLBACK_FUNCTION(Anna, function54);
+ ADD_CALLBACK_FUNCTION(Anna, function55);
+ ADD_CALLBACK_FUNCTION(Anna, function56);
+ ADD_CALLBACK_FUNCTION(Anna, function57);
+ ADD_CALLBACK_FUNCTION(Anna, function58);
+ ADD_CALLBACK_FUNCTION(Anna, function59);
+ ADD_CALLBACK_FUNCTION(Anna, function60);
+ ADD_CALLBACK_FUNCTION(Anna, function61);
+ ADD_CALLBACK_FUNCTION(Anna, function62);
+ ADD_CALLBACK_FUNCTION(Anna, function63);
+ ADD_CALLBACK_FUNCTION(Anna, baggage);
+ ADD_CALLBACK_FUNCTION(Anna, function65);
+ ADD_CALLBACK_FUNCTION(Anna, chapter4);
+ ADD_CALLBACK_FUNCTION(Anna, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function68);
+ ADD_CALLBACK_FUNCTION(Anna, function69);
+ ADD_CALLBACK_FUNCTION(Anna, function70);
+ ADD_CALLBACK_FUNCTION(Anna, function71);
+ ADD_CALLBACK_FUNCTION(Anna, function72);
+ ADD_CALLBACK_FUNCTION(Anna, function73);
+ ADD_CALLBACK_FUNCTION(Anna, chapter5);
+ ADD_CALLBACK_FUNCTION(Anna, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function76);
+ ADD_CALLBACK_FUNCTION(Anna, function77);
+ ADD_CALLBACK_FUNCTION(Anna, function78);
+ ADD_CALLBACK_FUNCTION(Anna, function79);
+ ADD_CALLBACK_FUNCTION(Anna, function80);
+ ADD_CALLBACK_FUNCTION(Anna, finalSequence);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Anna, reset)
+ Entity::reset(savepoint, true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Anna, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(3, Anna, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Anna, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Anna, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(6, Anna, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Anna, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Anna, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Anna, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Anna, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction) || getProgress().chapter >= kChapter2)
+ getSound()->playSound(kEntityPlayer, "CAT1001");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Anna, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Anna, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2 && ENTITY_PARAM(0, 1))
+ params->param2 = 1;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, 75)
+ getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound);
+
+ params->param6 = 0;
+ params->param7 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param4) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param4 = 0;
+ params->param5 = 1;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ --params->param1;
+
+ getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionEndSound:
+ if (params->param2) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ ++params->param1;
+
+ switch (params->param1) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityAnna, "ANN2135A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAnna, "ANN2135B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAnna, "ANN2135C");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAnna, "ANN2135C");
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityAnna, "ANN2135L");
+ break;
+
+ case 6:
+ getSound()->playSound(kEntityAnna, "ANN2135K");
+ break;
+
+ case 7:
+ getSound()->playSound(kEntityAnna, "ANN2135H");
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityAnna, "ANN2135K");
+ break;
+
+ case 9:
+ getSound()->playSound(kEntityAnna, "ANN2135I");
+ break;
+
+ case 10:
+ getSound()->playSound(kEntityAnna, "ANN2135J");
+ break;
+
+ case 11:
+ getSound()->playSound(kEntityAnna, "ANN2135M");
+ break;
+
+ case 12:
+ getSound()->playSound(kEntityAnna, "ANN2135L");
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param4) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ if (savepoint.param.intValue == 53) {
+ getSound()->playSound(kEntityPlayer, getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ if (rnd(2)) {
+ getSound()->playSound(kEntityPlayer, getSound()->wrongDoorCath());
+ } else {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1506A" : "CAT1506");
+ }
+ } else {
+ getSound()->playSound(kEntityPlayer, getSound()->wrongDoorCath());
+ }
+
+ params->param4 = 0;
+ params->param5 = 0;
+ } else {
+ getSound()->removeFromQueue(kEntityAnna);
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case kActionOpenDoor:
+ getSound()->removeFromQueue(kEntityAnna);
+ setCallback(3);
+ setup_playSound("LIB013");
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 49))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "418C");
+
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getSound()->playSound(kEntityAnna, "ANN2135A");
+ break;
+
+ case kActionDrawScene:
+ if (params->param5 || params->param4) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60)) {
+ ++params->param3;
+ if (params->param3 == 2) {
+ setCallback(2);
+ setup_draw("418B");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Ann1016");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorHand);
+ params->param4 = 1;
+ break;
+
+ case 3:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(4);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ --params->param1;
+ params->param6 = 1;
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAnna, "418A");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(13, Anna, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Anna, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Anna, function15, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param5 = 0;
+ params->param6 = 1;
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next action
+
+ case kActionKnock:
+ if (params->param5) {
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ if (savepoint.param.intValue == kObject53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ } else {
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ }
+ } else {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, (char *)&params->seq);
+ break;
+
+ case kActionDrawScene:
+ if (params->param6 || params->param5) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param5 = 0;
+ params->param6 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param5 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ params->param5 = 0;
+ params->param6 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Anna, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityAnna, kAction291662081, 0);
+ getSavePoints()->addData(kEntityAnna, kAction238936000, 1);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->inventoryItem = (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityAnna, kEntityPlayer, 2000)) ? (InventoryItem)LOW_BYTE(params->param3) : kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ if (savepoint.param.intValue == 8) {
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+ params->param3 &= 0xFFFFFFF7;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaGiveScarf);
+ } else {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventGotALight);
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction) || getProgress().chapter >= kChapter2)
+ getSound()->playSound(kEntityPlayer, "CAT1001");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityAnna);
+ break;
+
+ case kActionDefault:
+ if (getProgress().jacket == kJacketGreen) {
+ if (!getEvent(kEventGotALight) && !getEvent(kEventGotALightD) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ params->param3 = kItemInvalid;
+
+ if (!params->param3 && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param3 |= 8;
+ }
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf)
+ || getEvent(kEventAnnaGiveScarfDiner)
+ || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram)
+ || getEvent(kEventAnnaGiveScarfDinerMonogram)
+ || getEvent(kEventAnnaGiveScarfSalonMonogram))
+ getAction()->playAnimation(kEventAnnaGiveScarfAsk);
+ else if (getEvent(kEventAugustPresentAnna)
+ || getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ getAction()->playAnimation(kEventAnnaGiveScarfMonogram);
+ else
+ getAction()->playAnimation(kEventAnnaGiveScarf);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->direction == kDirectionUp ? kEventGotALightD : kEventGotALight);
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh);
+ params->param3 &= 0xFFFFFF7F;
+
+ if (getProgress().jacket == kJacketGreen && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param3 |= 8;
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && params->param1 < getState()->time && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5 && !params->param4) {
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 900)
+ params->param2 |= kItemScarf;
+ params->param5 = 0;
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ } else {
+ params->param7 = 0;
+ }
+ break;
+
+ case kAction1:
+ setCallback(savepoint.param.intValue == 8 ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, savepoint.param.intValue == 8 ? kEventAnnaGiveScarf : kEventDinerMindJoin);
+ break;
+
+ case kActionDefault:
+ if (getProgress().jacket == kJacketGreen) {
+ if (!getEvent(kEventDinerMindJoin) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ params->param2 |= kItemInvalid;
+
+ if (!params->param2 && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param2 |= 8;
+ }
+
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ break;
+
+ case kActionDrawScene:
+ params->param3 = getEntities()->isPlayerPosition(kCarRestaurant, 62);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram)) {
+ getAction()->playAnimation(kEventAnnaGiveScarfDinerAsk);
+ } else {
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaGiveScarfDinerMonogram : kEventAnnaGiveScarfDiner);
+ params->param5 = 1;
+ }
+
+ params->param2 &= 0xFFFFFFF7;
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventDinerMindJoin);
+
+ params->param2 &= 0xFFFFFFF7;
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk)) {
+ params->param2 |= 8;
+ }
+
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ params->param4 = 1;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ params->param4 = 0;
+ break;
+
+ case kAction170016384:
+ case kAction259136835:
+ case kAction268773672:
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Anna, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("618Ca", kObjectCompartment1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_8514;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Anna, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function15(kTime1093500, "NONE");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("618Bf", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Anna, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_draw("801US");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceRight(kEntityAnna, "001B");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 4:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Anna, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001A");
+ getSavePoints()->push(kEntityAnna, kEntityPascale, kAction223262556);
+ break;
+
+ case kAction157370960:
+ getData()->location = kLocationInsideCompartment;
+ setup_function23();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Anna, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001D");
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction270410280);
+ getSavePoints()->push(kEntityAnna, kEntityTables0, kAction136455232);
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001E");
+ setCallback(2);
+ setup_playSound("ANN1048");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_draw("001F");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction203859488);
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Anna, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001G");
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001H");
+ setCallback(2);
+ setup_playSound("ANN1049");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction136702400);
+ setup_function25();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Anna, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001J");
+ getProgress().field_28 = 1;
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 2:
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ setup_function26();
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+
+ case kAction201437056:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001J");
+ setCallback(2);
+ setup_function18(kTime1138500);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Anna, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62);
+
+ setCallback(1);
+ setup_callSavepoint("001L", kEntityTables0, kActionDrawTablesWithChairs, "001H");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62);
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction237485916);
+ getEntities()->drawSequenceRight(kEntityAnna, "801DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function17(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Anna, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ setCallback(1);
+ setup_function15(kTime1156500, "NONE");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (getProgress().field_14 == 29) {
+ params->param1 = (uint)(getState()->time + 900);
+ setCallback(2);
+ setup_function15((TimeValue)params->param1, "NONE");
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("618Bf", kObjectCompartmentF);
+ }
+ break;
+
+ case 3:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Anna, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_1540;
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ setCallback(3);
+ setup_updatePosition("104A", kCarRestaurant, 56);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ setup_function29();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Anna, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 900)
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf);
+ params->param2 = 0;
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param4 = 0;
+ }
+ break;
+
+ case kAction1:
+ setCallback(savepoint.param.intValue == 8 ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, savepoint.param.intValue == 8 ? kEventAnnaGiveScarf : kEventAnnaIntroductionRejected);
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaConversationGoodNight)
+ && !getEvent(kEventAnnaIntroductionRejected))
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getProgress().jacket == kJacketGreen
+ && getData()->inventoryItem == kItemNone
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf);
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "104B");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 56);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf)
+ || getEvent(kEventAnnaGiveScarfDiner)
+ || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram)
+ || getEvent(kEventAnnaGiveScarfDinerMonogram)
+ || getEvent(kEventAnnaGiveScarfSalonMonogram)) {
+ getAction()->playAnimation(kEventAnnaGiveScarfSalonAsk);
+ } else {
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaGiveScarfSalonMonogram : kEventAnnaGiveScarfSalon);
+ params->param2 = 1;
+ }
+
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+
+ case 2:
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaConversationGoodNight : kEventAnnaIntroductionRejected);
+
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getData()->inventoryItem = kItemNone;
+ setup_function30();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Anna, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 != kTimeInvalid && getState()->time) {
+ if (getState()->time > kTime1188000) {
+ params->param3 = kTimeInvalid;
+ getSound()->playSound(kEntityAnna, "AUG1004");
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)(getState()->time + 450);
+
+ if (params->param3 < getState()->time) {
+ params->param3 = kTimeInvalid;
+ getSound()->playSound(kEntityAnna, "AUG1004");
+ }
+ }
+ }
+
+ if (params->param2 && params->param4 != kTimeInvalid && getState()->time > kTime1179000) {
+
+ if (getState()->time > kTime1192500) {
+ params->param4 = kTimeInvalid;
+ setup_function30();
+ break;
+ }
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)(getState()->time + 150);
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ setup_function30();
+ break;
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ params->param2 = 1;
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAnna, "106B");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 56);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Anna, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityAnna, "AUG1005");
+
+ setCallback(2);
+ setup_updateFromTicks(150);
+ break;
+
+ case 2:
+ getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 56);
+
+ setCallback(3);
+ setup_draw2("106C1", "106C2", kEntityAugust);
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 56);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction159332865);
+
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Anna, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function33();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Anna, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+
+ params->param1 = (uint)(getState()->time + 4500);
+ setCallback(1);
+ setup_function15((TimeValue)params->param1, "NONE");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getObjects()->updateLocation2(kObjectCompartmentF, kObjectLocation1);
+ setup_function34();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Anna, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntities()->isPlayerPosition(kCarRedSleeping, 60)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 150)
+ setCallback(1);
+ setup_draw("419B");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_1:
+ TIME_CHECK(kTime1489500, params->param3, setup_function35);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "419A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ params->param1 = 1;
+ goto label_callback_1;
+
+ case 2:
+ case 3:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(4);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Anna, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ switch (params->param2) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityAnna, "ANN2135E");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityAnna, "ANN2135F");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAnna, "ANN2135G");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAnna, "ANN2135D");
+ break;
+ }
+
+ params->param1 = 0;
+ params->param3 = 0;
+ break;
+
+ case kActionEndSound:
+ ++params->param2;
+
+ if (params->param2 > 3)
+ params->param2 = 0;
+
+ params->param1 = 1;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaVisitToCompartmentGun);
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes1;
+ params->param1 = 1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaVisitToCompartmentGun);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_4840;
+
+ getEntities()->updateEntity(kEntityAnna, kCarRedSleeping, kPosition_8200);
+ getScenes()->loadSceneFromObject(kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityVassili, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityVerges, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+
+ setup_function36();
+ break;
+
+ case 2:
+ setup_function36();
+ break;
+ }
+ break;
+
+ case kAction226031488:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ break;
+
+ case kAction238358920:
+ setCallback(2);
+ setup_enterExitCompartment("608Cf", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Anna, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("608Aa", kObjectCompartmentA);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Anna, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction191477936:
+ setup_function38();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Anna, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+
+ setCallback(1);
+ setup_playSound("ANN1010");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSound()->playSound(kEntityPlayer, "MUS043");
+ setup_function40();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaGoodNight);
+ break;
+
+ case kActionExcuseMe:
+ getSound()->playSound(kEntityAnna, "ANN1107A");
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ if (!getEvent(kEventAnnaGoodNight) && !getEvent(kEventAnnaGoodNightInverse))
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(getData()->direction == kDirectionNone ? kEventAnnaGoodNight : kEventAnnaGoodNightInverse);
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Anna, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("608Cb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_function39(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("608Bf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(4);
+ setup_updateFromTime(150);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("608Cf", kObjectCompartmentF);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_function39(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("608Bb", kObjectCompartmentB);
+ break;
+
+ case 7:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(8);
+ setup_updateFromTime(150);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("608Cb", kObjectCompartmentB);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_function39(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("608Bf", kObjectCompartmentF);
+ break;
+
+ case 11:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4070;
+
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Anna, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 2700);
+
+ params->param5++;
+ switch (params->param5) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419A");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ params->param1 = 0;
+ break;
+ }
+
+ params->param2 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(3);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Anna, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Anna, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function15(kTime1786500, "418C");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function12();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function15(kTime1818000, "418C");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function12();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function15(kTimeEnd, "418C");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Anna, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(45, Anna, function45, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("625Bf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, params->param1 ? kAction185737168 : kAction185671840);
+ getSound()->playSound(kEntityAnna, "Ann3147");
+ getEntities()->drawSequenceLeft(kEntityAnna, "625EF");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction157894320:
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Anna, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function47();
+ } else {
+ setCallback(2);
+ setup_function15((TimeValue)(getState()->time + 4500), "418C");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Anna, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("688Bf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("801VS");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAnna, getEvent(kEventAugustLunch) ? "Ann3136" : "Ann3136A", SoundManager::kFlagInvalid, 30);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122358304);
+
+ setCallback(5);
+ setup_draw2("026B1", "026B2", kEntityAugust);
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ setup_function48();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Anna, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (params->param3 != kTimeInvalid && getState()->time > kTime1969200) {
+ UPDATE_PARAM_PROC_TIME(kTime1983600, (!getEntities()->isInRestaurant(kEntityPlayer) || getSound()->isBuffered(kEntityBoutarel)), params->param3, 150)
+ setCallback(3);
+ setup_playSound("Aug3007A");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 2)) {
+ if (!params->param2)
+ params->param2 = (uint)(getState()->time + 4500);
+
+ if (params->param4 != kTimeInvalid) {
+ if (params->param2 >= getState()->time) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)(getState()->time + 450);
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setup_function50();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "026C");
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Ann3137B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction218983616);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("Aug3006A");
+ break;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSound("Aug3006");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(2700);
+ break;
+
+ case 8:
+ getEntities()->drawSequenceLeft(kEntityAnna, "026H");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "026C");
+
+ setCallback(5);
+ setup_playSound("Ann3138A");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Anna, leaveTableWithAugust)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityAnna, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getEntities()->clearSequences(kEntityAugust);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables3, "026J3");
+ getEntities()->drawSequenceRight(kEntityAugust, "026J2");
+ getEntities()->drawSequenceRight(kEntityAnna, "026J1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Anna, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("ann3141");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(3);
+ setup_leaveTableWithAugust();
+ break;
+
+ case 3:
+ setup_function51();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Anna, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getSound()->playSound(kEntityAnna, "Aug3008");
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw2("112E1", "112E2", kEntityAugust);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityAnna, "Aug3142", SoundManager::kFlagInvalid, 30);
+ getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 57);
+ getEntities()->drawSequenceRight(kEntityAnna, "112A");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAnna, "112B");
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 57);
+ getSavePoints()->push(kEntityAnna, kEntityServers1, kAction219377792);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+
+ setup_function52();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "112D");
+
+ if (getState()->time >= kTimeEnterAttnangPuchheim) {
+ params->param1 = 1;
+ } else {
+ setCallback(4);
+ setup_playSound("Ann3142A");
+ }
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(1800);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound("Aug3007");
+ break;
+
+ case 6:
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101169422:
+ if (getEvent(kEventKronosVisit)) {
+ setCallback(3);
+ setup_updatePosition("112J", kCarRestaurant, 57);
+ break;
+ }
+
+ if (getState()->time >= kTimeEnterAttnangPuchheim) {
+ params->param1 = 1;
+ } else {
+ setCallback(4);
+ setup_playSound("Ann3142A");
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "112D");
+ getSavePoints()->push(kEntityAnna, kEntityKronos, kAction157159392);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Anna, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityAnna);
+
+ setup_function53();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceRight(kEntityAnna, "688Af");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070) || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentF);
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Anna, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_48 && params->param5 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(kTime2065500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param5, 150)
+ setup_function54();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 9000)
+ params->param4 = !params->param4;
+ getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A");
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 75);
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ }
+
+ params->param7 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next case
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (savepoint.param.intValue == 53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->clothes = kClothes2;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+
+ if (!params->param3 && (getEntities()->isPlayerPosition(kCarRedSleeping, 60) || getState()->time > kTime2034000)) {
+ params->param3 = 1;
+
+ setCallback(9);
+ setup_draw("416");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ }
+
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 9:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 78);
+ getEntities()->drawSequenceLeft(kEntityAnna, "417B");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Anna, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3) {
+ TIME_CHECK(kTime2079000, params->param5, setup_function55);
+
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 9000)
+ params->param4 = !params->param4;
+ getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A");
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 75);
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ }
+
+ params->param7 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next case
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (savepoint.param.intValue == 53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 78);
+
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction189750912);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ }
+
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4070;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ getEntities()->drawSequenceLeft(kEntityAnna, "417B");
+ break;
+ }
+ break;
+
+ case kAction123733488:
+ setCallback(9);
+ setup_enterExitCompartment("629Ef", kObjectCompartmentF);
+ break;
+
+ case kAction156049968:
+ getEntities()->drawSequenceLeft(kEntityAnna, "629DF");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case kAction253868128:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Anna, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getInventory()->setLocationAndProcess(kItemKey, kObjectLocation1);
+
+ setCallback(1);
+ setup_function45(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9270);
+ break;
+
+ case 2:
+ setup_function56();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, Anna, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function57();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, Anna, function57)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction191668032);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction205033696);
+ getEntities()->drawSequenceLeft(kEntityAnna, "625Ef");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "625Gf");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction169032608);
+ break;
+
+ case 4:
+ if (getSound()->isBuffered(kEntityAugust)) {
+ setCallback(4);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(5);
+ setup_playSound("Aug3009");
+ }
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityAnna, "Aug3009A");
+
+ setCallback(6);
+ setup_enterExitCompartment("628Bf", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+
+ setup_function59();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityAnna, "628Af");
+
+ if (getSound()->isBuffered(kEntityAugust)) {
+ setCallback(4);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(5);
+ setup_playSound("Aug3009");
+ }
+ break;
+
+ case kAction192063264:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ setup_function58();
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("625Ff", kObjectCompartmentF);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, Anna, function58)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaSearchingCompartment);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaSearchingCompartment);
+ getEntities()->clearSequences(kEntityAnna);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+ getSound()->playSound(kEntityAnna, "lib015");
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+ setup_function59();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, Anna, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getInventory()->hasItem(kItemKey) && params->param4 != kTimeInvalid && getState()->time > kTime2218500) {
+ if (getState()->time > kTime2248200) {
+ params->param4 = kTimeInvalid;
+ setup_function61();
+ break;
+ }
+
+ if (!params->param3
+ || (!getEntities()->isPlayerInCar(kCarRedSleeping)
+ && !getEntities()->isInSalon(kEntityPlayer)
+ && !getEntities()->isInRestaurant(kEntityPlayer))
+ || !params->param4)
+ params->param4 = (uint)getState()->time;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ setup_function61();
+ break;
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ CursorStyle style = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, style);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, style);
+
+ params->param1= 0;
+ params->param2 = 1;
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (savepoint.param.intValue == 53) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 78);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("ANN1016");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityAnna, kEntityTatiana, kAction100906246);
+ break;
+ }
+ break;
+
+ case kAction156622016:
+ if (params->param3) {
+ setCallback(8);
+ setup_function60();
+ }
+ break;
+
+ case kAction236241630:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_playSound("Ann1016A");
+ break;
+
+ case kAction236517970:
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, Anna, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction122358304);
+ getSound()->playSound(kEntityAnna, rnd(2) ? "Ann3126" : "Ann3127");
+
+ setCallback(1);
+ setup_enterExitCompartment("630Cf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("630Df", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAnna);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction189026624);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction156049968:
+ setCallback(3);
+ setup_enterExitCompartment("629EF", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, Anna, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getState()->timeDelta = 3;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeIndex, 0);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_function45(false);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("802US");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceRight(kEntityAnna, "802UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getEntities()->clearSequences(kEntityAnna);
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, Anna, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2259000 && !params->param2) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityAnna, kEntityVesna, kAction189299008);
+ setup_function63();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getProgress().field_54 = 1;
+ break;
+
+ case kAction235856512:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(63, Anna, function63)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction171843264);
+ break;
+
+ // Game over with Anna killed!
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaKilled);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2250000, kSceneGameOverAnnaDied, true);
+ }
+ break;
+
+ // Anna will get killed...
+ case kAction272177921:
+ if (getSound()->isBuffered("MUS012"))
+ getSound()->processEntry("MUS012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaKilled);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(64, Anna, baggage)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBaggageArgument);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaBaggageArgument);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, (EventIndex)kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightAnna);
+
+ if (params->param1)
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param1 == Fight::kFightEndLost);
+ else {
+ getState()->time = (TimeValue)(getState()->time + 1800);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBagagePart2);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAnnaBagagePart2);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 96);
+
+ getProgress().field_54 = 0;
+ getState()->time = kTime2266200;
+
+ setup_function65();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(65, Anna, function65)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_function15(kTimeEnd, "NONE");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(66, Anna, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(67, Anna, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 46)) {
+ UPDATE_PARAM_GOTO(params->param4, getState()->timeTicks, 30, label_next);
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+ }
+
+ params->param4 = 0;
+
+label_next:
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaConversation_34);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, "511B");
+ break;
+
+ case kActionDrawScene:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaConversation_34);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+
+ setup_function68();
+ break;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_playSound("ANN1016");
+ break;
+
+ case 4:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 5:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction191001984:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->inventoryItem = kItemNone;
+
+ setup_function69();
+ break;
+
+ case kAction219971920:
+ params->param3 = 1;
+ getData()->inventoryItem = kItemInvalid;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(68, Anna, function68)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ setCallback(1);
+ setup_function15(kTime2511900, "NONE");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kAction191001984:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ setup_function69();
+ break;
+
+ case kAction201431954:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(69, Anna, function69)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+
+ setup_function70();
+ break;
+ }
+
+ TIME_CHECK_CALLBACK(kTime2535300, params->param3, 4, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)) {
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+
+ setup_function70();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("127A", kCarRestaurant, 56);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAnna, "127B");
+ getSavePoints()->push(kEntityAnna, kEntityServers1, kAction258136010);
+ break;
+
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("127G", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ setup_function70();
+ break;
+ }
+ break;
+
+ case kAction100969180:
+ getEntities()->clearSequences(kEntityAnna);
+ params->param1 = 1;
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "127E");
+ getSavePoints()->push(kEntityAnna, kEntityAbbot, kAction203073664);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(70, Anna, function70)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function72(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function71();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+ setup_function73();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(71, Anna, function71)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->entityPosition = kPosition_4070;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityAnna, "625Af");
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 7)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 28)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 56))
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentF, true);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!getEvent(kEventAnnaTiredKiss)
+ && getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityAnna, 2000)
+ && getEntities()->hasValidFrame(kEntityAnna)
+ && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaTiredKiss);
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaTiredKiss);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 29);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventAnnaTired) || getEntities()->isWalkingOppositeToPlayer(kEntityAnna))
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaTired);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (!getEvent(kEventAnnaTired))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaTired);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(73, Anna, function73)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 == kTimeInvalid || params->param1 >= getState()->time)
+ break;
+
+ if (params->param2 >= getState()->time) {
+ if (!((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) && params->param3))
+ params->param3 = (uint)getState()->time;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTrainHijacked);
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound("LIB012");
+ break;
+
+ case kActionOpenDoor:
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaKissTrainHijacked);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getState()->timeDelta = 1;
+
+ params->param1 = (uint)(getState()->time + 4500);
+ params->param2 = (uint)(getState()->time + 9000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventTrainHijacked);
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction139254416);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("Ann4200");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventAnnaKissTrainHijacked);
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction139254416);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(74, Anna, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggageRear;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(75, Anna, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getProgress().field_C)
+ getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies2 : kEventAnnaBaggageTies);
+ else
+ getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies3 : kEventAnnaBaggageTies4);
+
+ getScenes()->loadSceneFromPosition(kCarBaggage, 8);
+ setup_function76();
+ }
+ break;
+
+ case kAction272177921:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBaggageTies);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(76, Anna, function76)
+ if (savepoint.action == kAction158480160)
+ setup_function77();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(77, Anna, function77)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime3645000 && !params->param2) {
+ params->param2 = 1;
+ getState()->timeDelta = 0;
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, savepoint.action == kActionKnock ? "LIB012" : "LIB014");
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaDialogGoToJerusalem);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject106, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 1;
+ break;
+
+ case 2:
+ getObjects()->update(kObject106, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getAction()->playAnimation(kEventAnnaDialogGoToJerusalem);
+
+ getState()->time = kTimeCityConstantinople;
+ getState()->timeDelta = 0;
+
+ getSavePoints()->push(kEntityAnna, kEntityTatiana, kAction236060709);
+
+ getScenes()->loadSceneFromPosition(kCarBaggage, 97, 1);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 3:
+ setup_function78();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(78, Anna, function78)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDrawScene:
+ if ((getEntities()->isInRestaurant(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer)) && getInventory()->hasItem(kItemFirebird)) {
+ setup_function80();
+ break;
+ }
+
+ getState()->time = kTimeInvalid2;
+
+ setCallback(getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? 2 : 1);
+ setup_savegame(kSavegameTypeEvent, getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? kEventKronosHostageAnna : kEventKronosHostageAnnaNoFirebird);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKronosHostageAnnaNoFirebird);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAugustUnhookCarsBetrayal, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosHostageAnna);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ getSound()->playSound(kEntityAnna, "Mus024", SoundManager::kFlagDefault);
+ setup_function79();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(79, Anna, function79)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getState()->time = kTime5933;
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInRestaurant(kEntityPlayer) && getInventory()->hasItem(kItemFirebird)) {
+ setup_function80();
+ break;
+ }
+
+ if (getEntities()->isInSalon(kEntityPlayer) && !getEvent(kEventKahinaPunch)) {
+ getState()->time = kTime5933;
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchSalon);
+ else if (getEntities()->isInRestaurant(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchRestaurant);
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchKitchen);
+ else if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchBaggageCarEntrance);
+ else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage))
+ getAction()->playAnimation(kEventKahinaPunchBaggageCar);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKahinaPunchSalon);
+ break;
+ }
+
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(80, Anna, function80)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 450);
+
+ getSound()->playSound(kEntityPlayer, "Kro5001", SoundManager::kFlagDefault);
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityPlayer, "Kro5002", SoundManager::kFlagDefault);
+ getState()->time = kTime4923000;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringFirebird);
+ break;
+
+ case kActionDefault:
+ getState()->time = kTime4929300;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getAction()->playAnimation(kEventKronosBringFirebird);
+ getScenes()->loadSceneFromItem(kItemFirebird);
+ getSound()->playSound(kEntityAnna, "Mus025", SoundManager::kFlagDefault);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKahinaPunch);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 3:
+ getProgress().isEggOpen = true;
+
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getAction()->playAnimation(kEventKronosOpenFirebird);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 3);
+
+ setup_finalSequence();
+ break;
+ }
+ break;
+
+ case kAction205294778:
+ getState()->time = kTime4929300;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosOpenFirebird);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(81, Anna, finalSequence)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 180);
+
+ getSound()->playSound(kEntityTrain, "LIB069");
+ getLogic()->gameOver(kSavegameTypeIndex, 2, kSceneNone, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCathCloseEggNoBackground);
+ getAction()->playAnimation(kEventKronosGiveFirebird);
+
+ if (getInventory()->hasItem(kItemWhistle))
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverTrainExplosion, true);
+ else if (getInventory()->get(kItemWhistle)->location == kObjectLocation1)
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAnnaDialogGoToJerusalem, kSceneNone, true);
+ else
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAugustUnhookCarsBetrayal, kSceneGameOverTrainExplosion2, true);
+ break;
+
+ case 2:
+ getInventory()->removeItem(kItemWhistle);
+ getLogic()->playFinalSequence();
+ break;
+ }
+ break;
+
+ case kAction224309120:
+ getProgress().isEggOpen = false;
+ getState()->time = kTimeCityConstantinople;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosGiveFirebird);
+ break;
+
+ case kActionUseWhistle:
+ getProgress().isEggOpen = false;
+ setGlobalTimer(0);
+ getState()->time = kTimeCityConstantinople;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventFinalSequence);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/anna.h b/engines/lastexpress/entities/anna.h
new file mode 100644
index 0000000000..5297d6f745
--- /dev/null
+++ b/engines/lastexpress/entities/anna.h
@@ -0,0 +1,252 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ANNA_H
+#define LASTEXPRESS_ANNA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Anna : public Entity {
+public:
+ Anna(LastExpressEngine *engine);
+ ~Anna() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ DECLARE_FUNCTION_2(function15, TimeValue timeValue, const char *sequence)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_2(function17, uint32, uint32)
+
+ DECLARE_FUNCTION_1(function18, TimeValue timeValue)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION_2(function39, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+ DECLARE_FUNCTION_1(function45, bool useAction1)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(leaveTableWithAugust)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+ DECLARE_FUNCTION(function57)
+ DECLARE_FUNCTION(function58)
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+ DECLARE_FUNCTION(function63)
+ DECLARE_FUNCTION(baggage)
+ DECLARE_FUNCTION(function65)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function68)
+ DECLARE_FUNCTION(function69)
+ DECLARE_FUNCTION(function70)
+ DECLARE_FUNCTION(function71)
+ DECLARE_FUNCTION_2(function72, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION(function73)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function76)
+ DECLARE_FUNCTION(function77)
+ DECLARE_FUNCTION(function78)
+ DECLARE_FUNCTION(function79)
+ DECLARE_FUNCTION(function80)
+ DECLARE_FUNCTION(finalSequence)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ANNA_H
diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp
new file mode 100644
index 0000000000..e9aff248e4
--- /dev/null
+++ b/engines/lastexpress/entities/august.cpp
@@ -0,0 +1,3536 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/august.h"
+
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+August::August(LastExpressEngine *engine) : Entity(engine, kEntityAugust) {
+ ADD_CALLBACK_FUNCTION(August, reset);
+ ADD_CALLBACK_FUNCTION(August, updateFromTime);
+ ADD_CALLBACK_FUNCTION(August, draw);
+ ADD_CALLBACK_FUNCTION(August, updatePosition);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(August, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(August, callSavepoint);
+ ADD_CALLBACK_FUNCTION(August, callSavepointNoDrawing);
+ ADD_CALLBACK_FUNCTION(August, draw2);
+ ADD_CALLBACK_FUNCTION(August, playSound);
+ ADD_CALLBACK_FUNCTION(August, playSound16);
+ ADD_CALLBACK_FUNCTION(August, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(August, savegame);
+ ADD_CALLBACK_FUNCTION(August, updateEntity);
+ ADD_CALLBACK_FUNCTION(August, function17);
+ ADD_CALLBACK_FUNCTION(August, updateEntity2);
+ ADD_CALLBACK_FUNCTION(August, function19);
+ ADD_CALLBACK_FUNCTION(August, function20);
+ ADD_CALLBACK_FUNCTION(August, function21);
+ ADD_CALLBACK_FUNCTION(August, chapter1);
+ ADD_CALLBACK_FUNCTION(August, function23);
+ ADD_CALLBACK_FUNCTION(August, dinner);
+ ADD_CALLBACK_FUNCTION(August, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(August, function26);
+ ADD_CALLBACK_FUNCTION(August, function27);
+ ADD_CALLBACK_FUNCTION(August, function28);
+ ADD_CALLBACK_FUNCTION(August, function29);
+ ADD_CALLBACK_FUNCTION(August, restaurant);
+ ADD_CALLBACK_FUNCTION(August, function31);
+ ADD_CALLBACK_FUNCTION(August, function32);
+ ADD_CALLBACK_FUNCTION(August, function33);
+ ADD_CALLBACK_FUNCTION(August, function34);
+ ADD_CALLBACK_FUNCTION(August, chapter2);
+ ADD_CALLBACK_FUNCTION(August, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(August, function37);
+ ADD_CALLBACK_FUNCTION(August, function38);
+ ADD_CALLBACK_FUNCTION(August, function39);
+ ADD_CALLBACK_FUNCTION(August, chapter3);
+ ADD_CALLBACK_FUNCTION(August, function41);
+ ADD_CALLBACK_FUNCTION(August, function42);
+ ADD_CALLBACK_FUNCTION(August, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(August, function44);
+ ADD_CALLBACK_FUNCTION(August, function45);
+ ADD_CALLBACK_FUNCTION(August, function46);
+ ADD_CALLBACK_FUNCTION(August, function47);
+ ADD_CALLBACK_FUNCTION(August, function48);
+ ADD_CALLBACK_FUNCTION(August, function49);
+ ADD_CALLBACK_FUNCTION(August, function50);
+ ADD_CALLBACK_FUNCTION(August, function51);
+ ADD_CALLBACK_FUNCTION(August, function52);
+ ADD_CALLBACK_FUNCTION(August, function53);
+ ADD_CALLBACK_FUNCTION(August, function54);
+ ADD_CALLBACK_FUNCTION(August, function55);
+ ADD_CALLBACK_FUNCTION(August, function56);
+ ADD_CALLBACK_FUNCTION(August, chapter4);
+ ADD_CALLBACK_FUNCTION(August, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(August, function59);
+ ADD_CALLBACK_FUNCTION(August, function60);
+ ADD_CALLBACK_FUNCTION(August, function61);
+ ADD_CALLBACK_FUNCTION(August, function62);
+ ADD_CALLBACK_FUNCTION(August, function63);
+ ADD_CALLBACK_FUNCTION(August, function64);
+ ADD_CALLBACK_FUNCTION(August, function65);
+ ADD_CALLBACK_FUNCTION(August, chapter5);
+ ADD_CALLBACK_FUNCTION(August, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(August, function68);
+ ADD_CALLBACK_FUNCTION(August, unhookCars);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, August, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, August, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, August, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(4, August, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, August, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, August, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarGreenSleeping, kObjectCompartment3, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, August, enterExitCompartment3, ObjectIndex)
+ if (savepoint.action == kAction4) {
+ getEntities()->exitCompartment(kEntityAugust, (ObjectIndex)params->param4);
+ CALLBACK_ACTION();
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, August, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(9, August, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(10, August, callSavepointNoDrawing, EntityIndex, ActionIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param6)
+ getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)&params->seq);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction10:
+ if (!params->param6) {
+ getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)&params->seq);
+ params->param6 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(11, August, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(12, August, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(13, August, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, August, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(15, August, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(16, August, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getProgress().eventMetAugust ? getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1002A" : "CAT1002") : getSound()->excuseMeCath();
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ params->param2 = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_updateEntity2(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_updateEntity2(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 1) = 0;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ ENTITY_PARAM(0, 1) = 1;
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(19, August, function19, bool, bool)
+ // Expose parameters as IISS and ignore the default exposed parameters
+ EntityData::EntityParametersIISS *parameters = (EntityData::EntityParametersIISS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, "AUG3101", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ strcpy((char *)&parameters->seq1, "626");
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (getData()->clothes != kClothes2) {
+ strcpy((char *)&parameters->seq1, "666");
+ break;
+ }
+ // Fallback to next action
+
+ case kChapter4:
+ case kChapter5:
+ strcpy((char *)&parameters->seq1, "696");
+ break;
+ }
+
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction303343617);
+
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+ strcat((char *)&parameters->seq2, "Pc");
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ setCallback(1);
+ setup_playSound("AUG2096");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+ strcat((char *)&parameters->seq2, "Qc");
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ if (parameters->param2)
+ getData()->inventoryItem = kItem147;
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+ strcat((char *)&parameters->seq2, parameters->param1 ? "Fc" : "Dc");
+
+ setCallback(3);
+ setup_enterExitCompartment((char *)&parameters->seq2, kObjectCompartment3);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAugust);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction69239528:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(20, August, function20, bool)
+ // Expose parameters as ISSI and ignore the default exposed parameters
+ EntityData::EntityParametersISSI *parameters = (EntityData::EntityParametersISSI*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ strcpy((char *)&parameters->seq1, "626");
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (getData()->clothes != kClothes2) {
+ strcpy((char *)&parameters->seq1, "666");
+ break;
+ }
+ // Fallback to next case
+
+ case kChapter4:
+ case kChapter5:
+ strcpy((char *)&parameters->seq1, "696");
+ break;
+ }
+
+ if (params->param1) {
+ strcpy((char *)&parameters->seq2, Common::String::format("%s%s", (char *)&parameters->seq1, "Gc").c_str());
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ } else {
+ strcpy((char *)&parameters->seq2, Common::String::format("%s%s", (char *)&parameters->seq1, "Ec").c_str());
+ }
+
+ setCallback(1);
+ setup_enterExitCompartment((char *)&parameters->seq2, kObjectCompartment3);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1: {
+ getData()->location = kLocationOutsideCompartment;
+
+ Common::String sequence2 = Common::String::format("%s%s", (char *)&parameters->seq2, "Pc");
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, sequence2.c_str());
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ if (getProgress().chapter != kChapter3 || getState()->time >= kTime1998000) {
+ setCallback(3);
+ setup_playSound("AUG2095");
+ } else {
+ setCallback(2);
+ setup_playSound("AUG2094");
+ }
+ }
+ break;
+
+ case 2:
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction269436673);
+ strcpy((char *)&parameters->seq2, Common::String::format("%s%s", (char *)&parameters->seq1, "Qc").c_str());
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ break;
+ }
+ break;
+
+ case kAction69239528:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(21, August, function21, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4 && params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM_GOTO(params->param8, getState()->timeTicks, 75, label_continue);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, (getProgress().eventMetAugust || getProgress().jacket != kJacketGreen) ? kCursorNormal : kCursorHand);
+ }
+
+ params->param8 = 0;
+
+label_continue:
+ if (getProgress().chapter != kChapter1)
+ break;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 6300)
+ params->param6 = 0;
+ CURRENT_PARAM(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!params->param4
+ && !getProgress().eventMetAugust
+ && !params->param6
+ && (params->param1 - 4500) > getState()->time
+ && !getProgress().field_14) {
+ getProgress().field_14 = 2;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_8200;
+
+ setCallback(1);
+ setup_function20(false);
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().chapter == kChapter1 && !getProgress().eventMetAugust && getProgress().jacket == kJacketGreen) {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustHisCompartment);
+ break;
+ }
+ // Fallback to next case
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(12);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1502" : "CAT1502A"));
+ } else {
+ setCallback(13);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 7 : 8);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function23((TimeValue)(params->param1 - 2700));
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(false, false);
+ break;
+
+ case 5:
+ if (getProgress().field_14 == 2)
+ getProgress().field_14 = 0;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ params->param5 = 0;
+ params->param6 = 0;
+ CURRENT_PARAM(1, 1) = 0;
+ break;
+
+ case 6:
+ getAction()->playAnimation(getObjects()->get(kObjectCompartment3).location2 == kObjectLocation1 ? kEventMeetAugustHisCompartmentBed : kEventMeetAugustHisCompartment);
+ getProgress().eventMetAugust = true;
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getScenes()->loadSceneFromObject(kObjectCompartment3, true);
+ break;
+
+ case 7:
+ case 8:
+ ++params->param5;
+
+ switch(params->param5) {
+ default:
+ // Fall to next case
+ break;
+
+ case 1:
+ setCallback(9);
+ setup_playSound(rnd(2) ? "AUG1128A" : "AUG1128B");
+ return;
+
+ case 2:
+ setCallback(10);
+ setup_playSound(getProgress().eventMetAugust ? "AUG1128E" : "AUG1128G");
+ return;
+
+ case 3:
+ setCallback(11);
+ setup_playSound(getProgress().eventMetAugust ? "AUG1128F" : "AUG1128H");
+ return;
+ }
+ // Fallback to next case
+
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorTalk, (getProgress().eventMetAugust || getProgress().jacket != kJacketGreen) ? kCursorNormal : kCursorHand);
+
+ if (getCallback() == 12 || getCallback() == 13) {
+ params->param2 = 0;
+ params->param3 = 1;
+ } else {
+ params->param2= 1;
+ }
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_updateFromTime(75);
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_playSound("AUG1128I");
+ break;
+
+ case 16:
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction100906246);
+ break;
+
+ case 17:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityAugust, "626Lc");
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+ break;
+
+ case 18:
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment; // BUG: in the original, this is set to 6470
+ getEntities()->clearSequences(kEntityAugust);
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param4 = 0;
+ break;
+ }
+ break;
+
+ case kAction124697504:
+ getSound()->playSound(kEntityAugust, "CON1023A");
+
+ setCallback(18);
+ setup_enterExitCompartment("626Mc", kObjectCompartment3);
+ break;
+
+ case kAction192849856:
+ setCallback(17);
+ setup_enterExitCompartment("626Kc", kObjectCompartment3);
+ break;
+
+ case kAction221617184:
+ params->param4 = 1;
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction102675536);
+
+ setCallback(14);
+ setup_playSound("CON1023");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, August, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject11, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+
+ getProgress().eventMetAugust = false;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29 || getProgress().field_14 == 3) {
+ if (params->param3) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_enterExitCompartment("626Ea", kObjectCompartment1);
+ } else {
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (!params->param2) {
+
+ if (!CURRENT_PARAM(1, 3))
+ CURRENT_PARAM(1, 3) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAM(1, 3) >= getState()->timeTicks)
+ break;
+
+ if (!params->param5) {
+ setCallback(8);
+ setup_playSound("AUG1002B");
+ break;
+ }
+
+label_callback_8:
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->timeTicks, 75)
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true);
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(9);
+ setup_enterExitCompartment("626Da", kObjectCompartment1);
+ } else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(10);
+ setup_enterExitCompartment3("626Da", kObjectCompartment1);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ break;
+ UPDATE_PARAM_PROC_END
+
+label_callback_9:
+ if (params->param3 && params->param1 < getState()->time && !CURRENT_PARAM(1, 5)) {
+ CURRENT_PARAM(1, 5) = 1;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(12);
+ setup_enterExitCompartment("626Ea", kObjectCompartment1);
+ }
+ break;
+ }
+
+ if (!CURRENT_PARAM(1, 1))
+ CURRENT_PARAM(1, 1) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAM(1, 1) >= getState()->timeTicks)
+ break;
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ params->param6++;
+
+ switch (params->param6) {
+ default:
+ break;
+
+ case 1:
+ setCallback(5);
+ setup_playSound("LIB013");
+ return;
+
+ case 2:
+ setCallback(7);
+ setup_playSound("LIB012");
+ return;
+
+ case 3:
+ params->param8++;
+
+ if (params->param8 >= 3) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ params->param6 = 0;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, params->param4 ? kCursorNormal : kCursorTalk, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ } else {
+
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? 8 : 7;
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustTylerCompartment);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param3) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(15);
+ setup_playSound("LIB012");
+ } else if (!params->param4) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(17);
+ setup_playSound("AUG1002A");
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ if (params->param3) {
+ getData()->location = kLocationInsideCompartment;
+
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMeetAugustHisCompartmentBed : kEventMeetAugustHisCompartment;
+ } else {
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMeetAugustTylerCompartmentBed : kEventMeetAugustTylerCompartment;
+ }
+
+ setCallback(14);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustTylerCompartment);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(13);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+
+ params->param2 = 1;
+ } else {
+ setCallback(1);
+ setup_enterExitCompartment("626Aa", kObjectCompartment1);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAugust, "626Ba");
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment1, true);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventAugustFindCorpse);
+ if (getEvent(kEventDinerAugustOriginalJacket))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventDinerAugustOriginalJacket, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ else if (getProgress().eventCorpseMovedFromFloor)
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ else
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getEntities()->clearSequences(kEntityAugust);
+ getData()->location = kLocationInsideCompartment;
+
+ getAction()->playAnimation((EventIndex)params->param7);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getProgress().eventMetAugust = true;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound16("AUG1002B");
+ break;
+
+ case 6:
+ case 7:
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, params->param4 ? kCursorNormal : kCursorTalk, kCursorHand);
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+
+ case 8:
+ params->param5 = 1;
+ goto label_callback_8;
+
+ case 9:
+ params->param3 = 1;
+ getEntities()->clearSequences(kEntityAugust);
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ goto label_callback_9;
+
+ case 10:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ break;
+
+ case 11:
+ getAction()->playAnimation(kEventAugustFindCorpse);
+
+ getLogic()->gameOver(getEvent(kEventDinerAugustOriginalJacket) ? kSavegameTypeEvent2 : kSavegameTypeIndex,
+ getEvent(kEventDinerAugustOriginalJacket) ? kEventDinerAugustOriginalJacket : 1,
+ getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice,
+ true);
+ break;
+
+ case 12:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+
+ case 13:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getAction()->playAnimation(kEventAugustFindCorpse);
+
+ if (getEvent(kEventDinerAugustOriginalJacket))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventDinerAugustOriginalJacket, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ else if (getProgress().eventCorpseMovedFromFloor)
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ else
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 14:
+ if (!params->param2)
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getAction()->playAnimation((EventIndex)params->param7);
+ getProgress().eventMetAugust = true;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_playSound("AUG1128A");
+ break;
+
+ case 16:
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 17:
+ params->param4 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, August, dinner)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventDinerAugust);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+
+ getAction()->playAnimation(getEntities()->isInRestaurant(kEntityAlexei) ? kEventDinerAugustAlexeiBackground : kEventDinerAugust);
+ getProgress().eventMetAugust = true;
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, August, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getProgress().eventCorpseFound) {
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction239072064);
+ params->param1 = 1;
+ }
+
+ if (getState()->time > kTime1080000 && !params->param3) {
+ params->param3 = 1;
+
+ if (!params->param1) {
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction239072064);
+ params->param1 = 1;
+ }
+ }
+
+ if (getState()->time > kTime1093500 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_callSavepoint("010J", kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ }
+ break;
+
+ case kAction1:
+ params->param2 = 0;
+ getData()->inventoryItem = kItemNone;
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction191604416);
+
+ if (getProgress().jacket == kJacketGreen) {
+ setCallback(3);
+ setup_dinner();
+ } else {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventDinerAugustOriginalJacket);
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAugust, kEntityTables3, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+
+ if (!getProgress().eventMetAugust)
+ params->param2 = kItemInvalid;
+
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction204704037);
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setup_function26();
+ break;
+
+ case 3:
+ setup_function28();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityAugust, kEntityAlexei, kAction225182640);
+ getAction()->playAnimation(kEventDinerAugustOriginalJacket);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityAugust, kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ getEntities()->drawSequenceRight(kEntityAugust, "010P");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction204704037);
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getProgress().field_14 = 2;
+
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function23(kTimeNone);
+ break;
+
+ case 8:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, August, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().eventMetAugust || getProgress().field_14) {
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ } else {
+ getProgress().field_14 = 2;
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function23((TimeValue)(getState()->time + 13500));
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(false, false);
+ break;
+
+ case 4:
+ if (getProgress().field_14 == 2)
+ getProgress().field_14 = 0;
+
+ setCallback(7);
+ setup_function21((TimeValue)(getState()->time + 900));
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function19(false, false);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function21((TimeValue)(getState()->time + 900));
+ break;
+
+ case 7:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, August, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, August, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ params->param1 = 0;
+
+ setCallback(3);
+ setup_dinner();
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventMetAugust && getProgress().jacket == kJacketGreen)
+ params->param1 = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction304061224);
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction203859488);
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ setup_function29();
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ break;
+
+ case kAction170016384:
+ getData()->inventoryItem = kItemNone;
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityAugust, "010G");
+
+ setCallback(2);
+ setup_playSound("AUG1053");
+ break;
+
+ case kAction268773672:
+ getData()->inventoryItem = kItemNone;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010D");
+
+ setCallback(1);
+ setup_playSound("AUG1052");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, August, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().field_28 || (params->param2 && params->param3 == kTimeInvalid))
+ break;
+
+ if (getState()->time < kTime1134000) {
+
+ if (!getEntities()->isInRestaurant(kEntityPlayer)
+ || getSound()->isBuffered("MRB1076") || getSound()->isBuffered("MRB1078") || getSound()->isBuffered("MRB1078A"))
+ params->param3 = (uint)getState()->time + 225;
+
+ if (params->param3 > getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+ getData()->inventoryItem = kItemNone;
+ getProgress().field_28 = 0;
+
+ setup_restaurant();
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ params->param1 = kItemNone;
+
+ setCallback(1);
+ setup_dinner();
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventMetAugust && getProgress().jacket == kJacketGreen)
+ params->param1 = kItemInvalid;
+
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param1);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "010H");
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param1);
+ break;
+
+ case kAction189426612:
+ params->param2 = 1;
+ break;
+
+ case kAction235257824:
+ params->param2 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, August, restaurant)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kAction1:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 62);
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 61);
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 64);
+ break;
+
+ case kActionEndSound:
+ if (params->param1) {
+ getData()->inventoryItem = kItemNone;
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 61);
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 64);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAugustPresentAnna);
+ break;
+ }
+
+ if (params->param2) {
+ params->param2 = 0;
+ if (getProgress().eventMetAugust)
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(kEntityAugust, "Aug1003A");
+ } else {
+ getData()->inventoryItem = kItemNone;
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction201437056);
+
+ setCallback(8);
+ setup_draw("010P");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAugust, kEntityBoutarel, kAction135854206);
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction259136835);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_callSavepoint("010N", kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction292758554);
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAugust, "001K");
+ getSound()->playSound(kEntityAugust, "AUG1003");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getProgress().field_60 = 1;
+
+ params->param2 = 1;
+ break;
+
+ case 4:
+
+ break;
+
+ case 5:
+ case 7:
+ case 9:
+ getSavePoints()->push(kEntityAugust, kEntityBoutarel, kAction134466544);
+
+ setup_function31();
+ break;
+
+ case 6:
+ case 8:
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(getCallback() + 1);
+ setup_callbackActionOnDirection();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, August, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function19(false, false);
+ break;
+
+ case 2:
+ setCallback(2);
+ setup_function21(kTime1161000);
+ break;
+
+ case 3:
+ case 4:
+ if (getProgress().field_14 == 29) {
+ setCallback(4);
+ setup_function21((TimeValue)(getState()->time + 900));
+ } else {
+ setup_function32();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, August, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC_TIME(kTime1179000, (!getEntities()->isInSalon(kEntityAnna) || getEntities()->isInSalon(kEntityPlayer)), params->param6, 0);
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592);
+ UPDATE_PARAM_PROC_END
+
+ if (params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ if (!params->param4) {
+ params->param4 = (uint)getState()->time + 1800;
+ params->param5 = (uint)getState()->time + 9000;
+ }
+
+ if (params->param7 != kTimeInvalid && params->param4 < getState()->time) {
+ UPDATE_PARAM_PROC_TIME(params->param5, getEntities()->isInSalon(kEntityPlayer), params->param7, 0);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("109D", kCarRestaurant, 56);
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57)) {
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ params->param3 = true;
+ } else if (!getEntities()->isPlayerPosition(kCarRestaurant, 50)) {
+ params->param3 = false;
+ }
+ } else {
+ params->param3 = getEntities()->isPlayerPosition(kCarRestaurant, 56) && params->param1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("105A", kCarRestaurant, 57);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B");
+ params->param2 = 1;
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function19(false, false);
+ break;
+
+ case 7:
+ setup_function33();
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ params->param2 = 0;
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction159332865:
+ getEntities()->drawSequenceLeft(kEntityAugust, "106E");
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, August, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getProgress().eventMetAugust ? 1 : 2);
+ setup_function21(getProgress().eventMetAugust ? (TimeValue)(getState()->time + 9000) : kTimeBedTime);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2)
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, August, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityAugust) && getProgress().field_18 != 4)
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, August, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_3970;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject11, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, August, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704);
+
+ if (getState()->time > kTime1773000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 62);
+
+ setCallback(2);
+ setup_callSavepoint("016C", kEntityTables0, kActionDrawTablesWithChairs, "016D");
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustGoodMorning);
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAugustGoodMorning))
+ getData()->inventoryItem = kItemInvalid;
+
+ getSavePoints()->push(kEntityAugust, kEntityTables0, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAugust, "016B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAugustGoodMorning);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 62);
+ getEntities()->drawSequenceRight(kEntityAugust, "803ES");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction286534136);
+
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(true, false);
+ break;
+
+ case 5:
+ setup_function37();
+ break;
+
+ case 6:
+ if (!getEvent(kEventAugustGoodMorning))
+ getData()->inventoryItem = kItemInvalid;
+
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction219522616);
+ getEntities()->drawSequenceLeft(kEntityAugust, "016B");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityAugust, "016A");
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_playSound("AUG2113");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, August, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1791000, params->param2, 5, setup_function20, true);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "506A2");
+ break;
+
+ case kActionDrawScene:
+ if (getState()->time > kTime1786500 && getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ if (params->param1) {
+ setCallback(2);
+ setup_draw("506C2");
+ } else {
+ params->param1 = 1;
+
+ setCallback(1);
+ setup_draw("506B2");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 16);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function20(true);
+ break;
+
+ case 3:
+ case 5:
+ setCallback(getCallback() + 1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 4:
+ case 6:
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, August, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1801800, params->param1, kEntityAugust, kEntityRebecca, kAction155980128);
+
+ TIME_CHECK_CALLBACK(kTime1820700, params->param2, 3, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("109A", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case 3:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("109D2", kCarRestaurant, 56);
+ break;
+
+ case 4:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ setCallback(5);
+ setup_function17(kTime1849500);
+ break;
+
+ case 5:
+ setup_function39();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSound("AUG2114");
+ break;
+
+ case 7:
+ getEntities()->drawSequenceLeft(kEntityAugust, "108C");
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 56);
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 57);
+
+ setCallback(8);
+ setup_playSound("AUG2114A");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_playSound("AUG2115");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_draw2("108D1", "108D2", kEntityRebecca);
+ break;
+
+ case 10:
+ getEntities()->drawSequenceLeft(kEntityAugust, "109B");
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 56);
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 57);
+ getSavePoints()->push(kEntityAugust, kEntityRebecca, kAction125496184);
+ break;
+ }
+ break;
+
+ case kAction169358379:
+ getSavePoints()->push(kEntityAugust, kEntityRebecca, kAction155465152);
+ getEntities()->drawSequenceLeft(kEntityAugust, "108A");
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, August, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!ENTITY_PARAM(0, 1))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustArrivalInMunich);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAugustArrivalInMunich);
+ getSavePoints()->push(kEntityAugust, kEntityChapters, kActionChapter3);
+ getEntities()->clearSequences(kEntityAugust);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, August, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!getEvent(kEventAugustMerchandise)
+ && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarGreenSleeping) {
+ getAction()->playAnimation(kEventAugustMerchandise);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ }
+ break;
+
+ case kAction1:
+ params->param3 = kItemNone;
+ getData()->inventoryItem = kItemNone;
+
+ getAction()->playAnimation((getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) ? kEventAugustTalkGoldDay : kEventAugustTalkGold);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getProgress().eventMetAugust)
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1002" : "CAT1002A");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityAugust);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEvent(kEventAugustMerchandise) && !getEvent(kEventAugustTalkGold) && !getEvent(kEventAugustTalkGoldDay))
+ params->param3 = kItemInvalid;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ params->param4 = 0;
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, getEvent(kEventAugustBringBriefcase) ? "AUG3103" : "AUG3100", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionExcuseMe:
+ if (!getSound()->isBuffered(kEntityAugust))
+ getSound()->excuseMe(kEntityAugust);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param3) {
+ params->param4 = 128;
+
+ if (!getEvent(kEventAugustBringBriefcase))
+ params->param4 = 147;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, August, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081);
+
+ // Set as same position as Anna
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityAnna)->entityPosition;
+ getData()->car = getEntityData(kEntityAnna)->car;
+ }
+
+ if (getState()->time > kTime2016000 && !params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function44();
+ }
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventAugustLunch);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function41(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803VS");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A2");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B2");
+
+ if (!getEvent(kEventAugustLunch))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventAugustLunch);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ params->param1 = 0;
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "112G");
+ break;
+
+ case kAction122358304:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, August, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("122H", kCarRestaurant, 57);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAugustMerchandise)) {
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ } else {
+ setCallback(2);
+ setup_function17(kTime2043000);
+ }
+ break;
+
+ case 2:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ } else {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustMerchandise);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustMerchandise);
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityAugust, kPosition_6470, 500))
+ getData()->entityPosition = kPosition_5970;
+
+ getEntities()->updateEntity(kEntityAugust, kCarGreenSleeping, kPosition_6470);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car,
+ (EntityPosition)(getData()->entityPosition + 750 * (getData()->direction == kDirectionUp ? -1 : 1)),
+ getData()->direction == kDirectionUp);
+
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(true, false);
+ break;
+
+ case 5:
+ setup_function45();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, August, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2061000 && !params->param1) {
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+
+ setup_function46();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, "AUG3102", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "506A2");
+ getData()->inventoryItem = kItem146; // TODO which item is that?
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, August, function46)
+ switch (savepoint.action) {
+ default:
+ TIME_CHECK_CALLBACK(kTime2088000, params->param1, 1, setup_function47);
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ setCallback(2);
+ setup_draw("507B2");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setup_function48();
+ break;
+
+ case 2:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 43))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 34);
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, August, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function41(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAugust);
+ setCallback(3);
+ setup_updateFromTime(2700);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(false, false);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, August, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeCityLinz, params->param1, setup_function49);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getEvent(kEventAugustTalkCompartmentDoor) && !getEvent(kEventAugustTalkCompartmentDoorBlueRedingote)
+ && !getEvent(kEventAugustBringEgg) && !getEvent(kEventAugustBringBriefcase)) {
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustTalkCompartmentDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getData()->clothes = kClothes2;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAugustTalkCompartmentDoor);
+ getScenes()->processScene();
+
+ setCallback(2);
+ setup_function21(kTimeCityLinz);
+ break;
+
+ case 2:
+ setup_function49();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, August, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 2:
+ setup_function50();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, August, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function51();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, August, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function42(kCarGreenSleeping, kPosition_5790, false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityTatiana, kAction191668032);
+
+ setCallback(2);
+ setup_function42(kCarRedSleeping, kPosition_540, true);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592);
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ setup_function52();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction169032608:
+ setCallback(3);
+ setup_function42(kCarRedSleeping, kPosition_3820, true);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, August, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getInventory()->hasItem(kItemBriefcase)) {
+ getData()->location = kLocationInsideCompartment;
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringBriefcase);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird) && !getEvent(kEventAugustBringEgg)) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringEgg);
+ break;
+ }
+
+ if (!getEvent(kEventAugustTalkCompartmentDoorBlueRedingote) && !getEvent(kEventAugustBringEgg) && !getEvent(kEventAugustBringBriefcase)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringEgg);
+ break;
+ }
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 6 : 7);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function42(kCarGreenSleeping, kPosition_6470, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function19(false, true);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityAugust, kEntityKahina, kAction134611040);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustBringBriefcase);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function17);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 13);
+
+ setup_function53();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventAugustBringEgg);
+ getScenes()->processScene();
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventAugustTalkCompartmentDoorBlueRedingote);
+ getScenes()->processScene();
+ break;
+
+ case 6:
+ case 7:
+ setCallback(8);
+ setup_playSound("AUG1128F");
+ break;
+
+ case 8:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, August, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(2700);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function20(false);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setup_function54();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, August, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4 || params->param2 || getProgress().field_44)
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (!params->param2 && params->param1) {
+ UPDATE_PARAM(params->param5, getState()->time, params->param1);
+
+ getData()->inventoryItem = kItemNone;
+ setup_function55();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustTalkCigar);
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 57);
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B3");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isPlayerPosition(kCarRestaurant, 60) || params->param3) {
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ } else if (!params->param2) {
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 57);
+ getEntities()->drawSequenceRight(kEntityAugust, "105C3");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("105A3", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAugust, kEntityAbbot, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B3");
+ params->param4 = 1;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustTalkCigar);
+ getEntities()->drawSequenceLeft(kEntityAugust, params->param3 ? "122B" : "105B3");
+ getScenes()->processScene();
+
+ params->param1 = 9000;
+ params->param4 = 0;
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ params->param2 = 0;
+
+ if (getEvent(kEventAugustTalkCigar))
+ params->param1 = 9000;
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ params->param2 = 1;
+ params->param3 = 1;
+ break;
+
+ case kAction136196244:
+ params->param2 = 1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, August, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("105D3", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(true, false);
+ break;
+
+ case 4:
+ setup_function56();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, August, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "507A3");
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ setCallback(1);
+ setup_draw("507B3");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityAugust, "507A3");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, August, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, August, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803WS");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A3");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function59();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, August, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction190605184);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction123793792:
+ setup_function60();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, August, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ bool pushSavepoint = false;
+ if (!params->param2) {
+ pushSavepoint = true;
+ params->param2 = (uint)getState()->time + 450;
+ }
+
+ if (params->param2 < getState()->time) {
+ pushSavepoint = true;
+ params->param2 = kTimeInvalid;
+ }
+
+ if (pushSavepoint)
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction207330561);
+
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param3, getState()->time, 9000);
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_callSavepoint("010J3", kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction286403504);
+ setup_function61();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction201964801:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010H3");
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, August, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->drawSequenceRight(kEntityAugust, "803FS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function19(false, false);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function21((TimeValue)(getState()->time + 4500));
+ break;
+
+ case 4:
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, August, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 900);
+
+ getSound()->playSound(kEntityAugust, "Aug4003A");
+
+ setCallback(5);
+ setup_updatePosition("122C", kCarRestaurant, 57);
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("696Ec", kObjectCompartment3);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("122A", kCarRestaurant, 57);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ getSavePoints()->push(kEntityAugust, kEntityServers1, kAction291721418);
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction125826561:
+ setup_function63();
+ break;
+
+ case kAction134486752:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(63, August, function63)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 1800)
+ getData()->inventoryItem = kItemInvalid;
+ UPDATE_PARAM_PROC_END
+
+ if (getState()->time > kTime2488500 && !params->param4) {
+ params->param4 = 1;
+ getData()->inventoryItem = kItemNone;
+ setup_function64();
+ break;
+ }
+
+ UPDATE_PARAM(params->param5, getState()->timeTicks, params->param1);
+
+ params->param2 = (params->param6 < 1 ? 1 : 0);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, params->param2 ? "122H" : "122F");
+
+ params->param1 = 5 * (3 * rnd(20) + 15);
+ params->param5 = 0;
+ break;
+
+ case kAction1:
+ if (getEntities()->isInSalon(kEntityAlexei))
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function44);
+
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustDrink);
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(20) + 15);
+ getEntities()->drawSequenceLeft(kEntityAugust, "122F");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAugustDrink);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+
+ setup_function64();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(64, August, function64)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ params->param1 = (uint)getState()->time + 1800;
+
+ if (params->param1 >= getState()->time)
+ break;
+
+ if (getState()->time > kTime2430000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("122J", kCarRestaurant, 57);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122H");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment2("696Dc", kObjectCompartment3);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAugust);
+ setup_function65();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(65, August, function65)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAugust);
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (!getSound()->isBuffered(kEntityAugust))
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(66, August, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(67, August, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function68();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(68, August, function68)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ ++params->param3;
+
+ switch (params->param3) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound("Aug5002");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_playSound("Aug5002A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("Aug5002B");
+ break;
+ }
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 6:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction203078272:
+ getSavePoints()->push(kEntityAugust, kEntityTatiana, kAction203078272);
+
+ setup_unhookCars();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(69, August, unhookCars)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getSavePoints()->pushAll(kEntityAugust, kAction135800432);
+ setup_nullfunction();
+ break;
+
+ case kActionDefault:
+ getSound()->processEntries();
+ if (getSound()->isBuffered("ARRIVE"))
+ getSound()->removeFromQueue("ARRIVE");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustUnhookCarsBetrayal);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(getProgress().field_C ? kEventAugustUnhookCarsBetrayal : kEventAugustUnhookCars);
+ getEntities()->clearSequences(kEntityAugust);
+ getSound()->resetState();
+ getSound()->playSound(kEntityPlayer, "MUS050");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 85, 1);
+ getSavePoints()->pushAll(kEntityAugust, kActionProceedChapter5);
+
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function42)
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(70, August)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/august.h b/engines/lastexpress/entities/august.h
new file mode 100644
index 0000000000..1a883a0ed5
--- /dev/null
+++ b/engines/lastexpress/entities/august.h
@@ -0,0 +1,275 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_AUGUST_H
+#define LASTEXPRESS_AUGUST_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class August : public Entity {
+public:
+ August(LastExpressEngine *engine);
+ ~August() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment3, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Call a savepoint
+ *
+ * @param param1 The entity
+ * @param param2 The action
+ * @param seq The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_3(callSavepointNoDrawing, EntityIndex entity, ActionIndex action, const char *sequence)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function17, TimeValue timeValue)
+
+ /**
+ * Updates the entity
+ *
+ * @param param1 The car
+ * @param param2 The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_2(function19, bool, bool)
+
+ DECLARE_FUNCTION_1(function20, bool)
+ DECLARE_FUNCTION_1(function21, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_1(function23, TimeValue timeValue)
+ DECLARE_FUNCTION(dinner)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(restaurant)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION_2(function41, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_3(function42, CarIndex car, EntityPosition entityPosition, bool)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+ DECLARE_FUNCTION(function63)
+ DECLARE_FUNCTION(function64)
+ DECLARE_FUNCTION(function65)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function68)
+ DECLARE_FUNCTION(unhookCars)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_AUGUST_H
diff --git a/engines/lastexpress/entities/boutarel.cpp b/engines/lastexpress/entities/boutarel.cpp
new file mode 100644
index 0000000000..b4378c95c4
--- /dev/null
+++ b/engines/lastexpress/entities/boutarel.cpp
@@ -0,0 +1,1260 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/boutarel.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Boutarel::Boutarel(LastExpressEngine *engine) : Entity(engine, kEntityBoutarel) {
+ ADD_CALLBACK_FUNCTION(Boutarel, reset);
+ ADD_CALLBACK_FUNCTION(Boutarel, playSound);
+ ADD_CALLBACK_FUNCTION(Boutarel, draw);
+ ADD_CALLBACK_FUNCTION(Boutarel, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Boutarel, updatePosition);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Boutarel, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Boutarel, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Boutarel, updateEntity);
+ ADD_CALLBACK_FUNCTION(Boutarel, function11);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterTableWithMmeBoutarel);
+ ADD_CALLBACK_FUNCTION(Boutarel, leaveTableWithMmeBoutarel);
+ ADD_CALLBACK_FUNCTION(Boutarel, function14);
+ ADD_CALLBACK_FUNCTION(Boutarel, function15);
+ ADD_CALLBACK_FUNCTION(Boutarel, function16);
+ ADD_CALLBACK_FUNCTION(Boutarel, function17);
+ ADD_CALLBACK_FUNCTION(Boutarel, function18);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter1);
+ ADD_CALLBACK_FUNCTION(Boutarel, function20);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function22);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter2);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function25);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter3);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function28);
+ ADD_CALLBACK_FUNCTION(Boutarel, function29);
+ ADD_CALLBACK_FUNCTION(Boutarel, function30);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter4);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function33);
+ ADD_CALLBACK_FUNCTION(Boutarel, function34);
+ ADD_CALLBACK_FUNCTION(Boutarel, function35);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter5);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function38);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Boutarel, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Boutarel, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Boutarel, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Boutarel, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(5, Boutarel, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Boutarel, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, Boutarel, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarRedSleeping, kObjectCompartmentC, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Boutarel, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Boutarel, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Boutarel, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+
+ if (getInventory()->hasItem(kItemPassengerList) && getState()->time > kTime1089000)
+ getSound()->playSound(kEntityPlayer, "CAT1022");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Boutarel, function11, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1) && ENTITY_PARAM(0, 2)) {
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 1) = 0;
+
+ setCallback(5);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ if (params->param1) {
+ if (getProgress().chapter == kChapter4) {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("607Hc", kObjectCompartmentC);
+ } else {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("607Dc", kObjectCompartmentC);
+ }
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("607Bc", kObjectCompartmentC);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 2:
+ case 3:
+ if (getCallback() == 2)
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ else
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ // Fallback to next case
+
+ case 1:
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityBoutarel, kEntityFrancois, kAction101107728);
+
+ setCallback(4);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityBoutarel);
+ break;
+
+ case 5:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_draw("812US");
+ break;
+
+ case 6:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getSound()->playSound(kEntityBoutarel, "MRB1075", SoundManager::kFlagInvalid, 60);
+ break;
+
+ case kChapter3:
+ getSound()->playSound(kEntityBoutarel, "MRB3101");
+ break;
+ }
+
+ setCallback(7);
+ setup_enterTableWithMmeBoutarel();
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Boutarel, enterTableWithMmeBoutarel)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getSavePoints()->push(kEntityBoutarel, kEntityTables2, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables2, "008A3");
+ getEntities()->drawSequenceRight(kEntityMmeBoutarel, "008A2");
+ getEntities()->drawSequenceRight(kEntityBoutarel, "008A1");
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->updateFrame(kEntityBoutarel);
+ getEntityData(kEntityMmeBoutarel)->location = getData()->location;
+ getEntityData(kEntityTables2)->location = getData()->location;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Boutarel, leaveTableWithMmeBoutarel)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityBoutarel, kEntityTables2, kActionDrawTablesWithChairs, "008F");
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables2, "008E3");
+ getEntities()->drawSequenceRight(kEntityMmeBoutarel, "008E2");
+ getEntities()->drawSequenceRight(kEntityBoutarel, "008E1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Boutarel, function14, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityBoutarel, "MRB1079");
+
+ setCallback(2);
+ setup_leaveTableWithMmeBoutarel();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction326144276);
+ getEntities()->drawSequenceRight(kEntityBoutarel, "812DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityBoutarel);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntityData(kEntityFrancois)->entityPosition = kPosition_540;
+ getEntityData(kEntityMmeBoutarel)->entityPosition = kPosition_540;
+ getData()->entityPosition = kPosition_540;
+
+ getEntityData(kEntityFrancois)->location = kLocationOutsideCompartment;
+ getEntityData(kEntityMmeBoutarel)->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityBoutarel);
+ getSavePoints()->push(kEntityBoutarel, kEntityMmeBoutarel, kAction100901266);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityBoutarel, kEntityFrancois, kAction100901266);
+
+ setCallback(5);
+ setup_updateFromTime(450);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ setCallback(params->param1 ? 7: 8);
+ setup_enterExitCompartment2(params->param1 ? "607Gc" : "607Ac", kObjectCompartmentC);
+ break;
+
+ case 7:
+ case 8:
+ getEntities()->clearSequences(kEntityBoutarel);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Boutarel, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (params->param1)
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(params->param1 ? 1 : 2);
+ setup_enterExitCompartment(params->param1 ? "607Dc" : "607Bc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition(params->seq, kCarRestaurant, 52);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters:
+// bool
+// const char *
+IMPLEMENT_FUNCTION_IS(16, Boutarel, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition((const char *)&params->seq, kCarRestaurant, 52);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(params->param1 ? 4 : 5);
+ setup_enterExitCompartment2(params->param1 ? "607Gc" : "607Ac", kObjectCompartmentC);
+ break;
+
+ case 4:
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(17, Boutarel, function17, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_ACTION(params->param1, params->param6);
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 90)
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ } else {
+ params->param7 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, (char *)&params->seq);
+ break;
+
+ case kActionDrawScene:
+ params->param5 = (getEntities()->isPlayerPosition(kCarRestaurant, 52) ? 1 : 0);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(18, Boutarel, function18, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param4) {
+ params->param4 = 1;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (savepoint.param.intValue == 50) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1511" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "MRB1001" : "MRB1001A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+
+ params->param2 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction123199584);
+ break;
+
+ }
+ break;
+
+ case kAction122865568:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction88652208);
+ break;
+
+ case kAction221683008:
+ setCallback(7);
+ setup_playSound("MRB1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Boutarel, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityBoutarel, kAction203520448, 0);
+ getSavePoints()->addData(kEntityBoutarel, kAction237889408, 1);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject42, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_1750;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Boutarel, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (!params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 4500)
+ setCallback(3);
+ setup_playSound("MRB1078A");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+
+ setCallback(2);
+ setup_playSound("MRB1076");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ break;
+
+ case 3:
+ TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityBoutarel, kEntityCooks, kAction224849280);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction134466544:
+ params->param2 = 0;
+ break;
+
+ case kAction135854206:
+ params->param2 = 1;
+ break;
+
+ case kAction168717392:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+
+ if (!params->param2) {
+ setCallback(5);
+ setup_playSound("MRB1078");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Boutarel, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kTime1071000, "101A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16(false, "101B");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18(kTime1102500);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 54) || getEntities()->isPlayerPosition(kCarRedSleeping, 44))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 10);
+
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 54);
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 44);
+
+ setCallback(4);
+ setup_playSound("MRB1074");
+ break;
+
+ case 4:
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 54);
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 44);
+
+ setCallback(5);
+ setup_function20();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function18(kTimeEnterChalons);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function15(false, "102A");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function17(kTime1183500, "102B");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function16(false, "102C");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function18(kTime1215000);
+ break;
+
+ case 10:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Boutarel, function22)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityBoutarel);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Boutarel, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Boutarel, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1759500, params->param2, 1, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInRestaurant(kEntityPlayer) && !params->param1) {
+ getSound()->playSound(kEntityBoutarel, "MRB2001");
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function25();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Boutarel, function25)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Boutarel, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Boutarel, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122288808:
+ setup_function28();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Boutarel, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(true);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function29();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Boutarel, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 450)
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ UPDATE_PARAM_PROC_END
+
+ if (!params->param1)
+ break;
+
+ if (getEntities()->isInRestaurant(kEntityAnna)
+ && getEntities()->isInRestaurant(kEntityAugust)
+ && !getSound()->isBuffered(kEntityBoutarel)
+ && params->param3 != kTimeInvalid) {
+
+ if (getState()->time <= kTime1998000)
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)(getState()->time + 450);
+
+ if (params->param3 < getState()->time || getState()->time > kTime1998000) {
+ params->param3 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("MRB3102");
+ break;
+ }
+ }
+
+ TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true);
+ break;
+
+ case 2:
+ setup_function30();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ params->param1 = 1;
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Boutarel, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Boutarel, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Boutarel, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2367000, params->param1, setup_function33);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Boutarel, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1)
+ TIME_CHECK_CALLBACK_1(kTime2389500, params->param2, 3, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+
+ setCallback(2);
+ setup_updateFromTime(450);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ break;
+
+ case 3:
+ setup_function34();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Boutarel, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2470500, params->param1, setup_function35);
+
+ if (getState()->time > kTime2457000 && getEvent(kEventAugustDrink)) {
+ getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction159003408);
+
+ setCallback(1);
+ setup_function15(false, "102A");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction101687594);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(kTime2479500, "102B");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(false, "102C");
+ break;
+
+ case 3:
+ case 7:
+ setup_function35();
+ break;
+
+ case 4:
+ case 8:
+ if (getState()->time >= kTime2470500) {
+ setup_function35();
+ break;
+ }
+
+ if (getEvent(kEventAugustDrink)) {
+ setCallback(5);
+ setup_function15(false, "102A");
+ } else {
+ setCallback(8);
+ setup_function18((TimeValue)(getState()->time + 900));
+ }
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17(kTime2479500, "102B");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function16(false, "102C");
+ break;
+
+ case 9:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction123199584);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction88652208);
+ break;
+
+ case kAction125039808:
+ setCallback(4);
+ setup_function18(kTime2457000);
+ break;
+
+ case kAction221683008:
+ setCallback(9);
+ setup_playSound("Mrb1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Boutarel, function35)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Boutarel, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Boutarel, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function38();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Boutarel, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(39, Boutarel)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/boutarel.h b/engines/lastexpress/entities/boutarel.h
new file mode 100644
index 0000000000..0ac051df77
--- /dev/null
+++ b/engines/lastexpress/entities/boutarel.h
@@ -0,0 +1,188 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_BOUTAREL_H
+#define LASTEXPRESS_BOUTAREL_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Boutarel : public Entity {
+public:
+ Boutarel(LastExpressEngine *engine);
+ ~Boutarel() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function11, bool)
+ DECLARE_FUNCTION(enterTableWithMmeBoutarel)
+ DECLARE_FUNCTION(leaveTableWithMmeBoutarel)
+ DECLARE_FUNCTION_1(function14, bool)
+ DECLARE_FUNCTION_2(function15, bool, const char *sequence)
+ DECLARE_FUNCTION_2(function16, bool, const char *sequence)
+ DECLARE_FUNCTION_2(function17, TimeValue timeValue, const char *sequence)
+ DECLARE_FUNCTION_1(function18, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function38)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BOUTAREL_H
diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp
new file mode 100644
index 0000000000..a057da23d3
--- /dev/null
+++ b/engines/lastexpress/entities/chapters.cpp
@@ -0,0 +1,1820 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/chapters.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/alouan.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/boutarel.h"
+#include "lastexpress/entities/coudert.h"
+#include "lastexpress/entities/cooks.h"
+#include "lastexpress/entities/francois.h"
+#include "lastexpress/entities/gendarmes.h"
+#include "lastexpress/entities/hadija.h"
+#include "lastexpress/entities/ivo.h"
+#include "lastexpress/entities/kahina.h"
+#include "lastexpress/entities/kronos.h"
+#include "lastexpress/entities/mahmud.h"
+#include "lastexpress/entities/max.h"
+#include "lastexpress/entities/mertens.h"
+#include "lastexpress/entities/milos.h"
+#include "lastexpress/entities/mmeboutarel.h"
+#include "lastexpress/entities/pascale.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/servers0.h"
+#include "lastexpress/entities/servers1.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tatiana.h"
+#include "lastexpress/entities/vassili.h"
+#include "lastexpress/entities/verges.h"
+#include "lastexpress/entities/vesna.h"
+#include "lastexpress/entities/yasmin.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+Chapters::Chapters(LastExpressEngine *engine) : Entity(engine, kEntityChapters) {
+ ADD_CALLBACK_FUNCTION(Chapters, savegame);
+ ADD_CALLBACK_FUNCTION(Chapters, enterStation);
+ ADD_CALLBACK_FUNCTION(Chapters, exitStation);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1);
+ ADD_CALLBACK_FUNCTION(Chapters, resetMainEntities);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1End);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Next);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, viennaEvents);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter5);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Handler);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(1, Chapters, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Chapters, enterStation, CityIndex)
+ enterExitStation(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Chapters, exitStation)
+ enterExitStation(savepoint, false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Chapters, chapter1)
+ if (savepoint.action == kActionDefault) {
+ getSavePoints()->addData(kEntityChapters, kAction171843264, 0);
+ setup_chapter1Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Chapters, resetMainEntities)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_reset);
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_reset);
+ RESET_ENTITY_STATE(kEntityAlouan, Alouan, setup_reset);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_reset);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_reset);
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_reset);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_reset);
+ RESET_ENTITY_STATE(kEntityFrancois, Francois, setup_reset);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_reset);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_reset);
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_reset);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_reset);
+ RESET_ENTITY_STATE(kEntityMahmud, Mahmud, setup_reset);
+ RESET_ENTITY_STATE(kEntityMax, Max, setup_reset);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_reset);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_reset);
+ RESET_ENTITY_STATE(kEntityGendarmes, Gendarmes, setup_reset);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_reset);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_reset);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_reset);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_reset);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_reset);
+ RESET_ENTITY_STATE(kEntityVassili, Vassili, setup_reset);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_reset);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_reset);
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Chapters, chapter1End)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityChapters, "MUS0009", SoundManager::kFlagDefault);
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ if (params->param1) {
+ getEntities()->clearSequences(kEntityChapters);
+ getSound()->processEntry(kEntityChapters);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getSound()->resetState();
+
+ ENTITY_PARAM(0, 4) = 7;
+
+ getSound()->playSteam(kCityPolice);
+
+ getAction()->playAnimation(kEventCathDream);
+
+ getState()->timeDelta = 3;
+ getProgress().field_18 = 1;
+ getProgress().field_84 = 0;
+
+ getObjects()->update(kObject63, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ } else {
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getSound()->playSound(kEntityPlayer, "LIB015", SoundManager::kFlagDefault, 15);
+
+ if (!getSound()->isBuffered(kEntityChapters))
+ getSound()->playSound(kEntityChapters, "MUS009", SoundManager::kFlagDefault);
+
+ getScenes()->loadSceneFromPosition(kCarLocomotive, 38);
+
+ getObjects()->update(kObject63, kEntityChapters, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ RESET_ENTITY_STATE(kEntityPascale, Pascale, setup_function19);
+ RESET_ENTITY_STATE(kEntityServers0, Servers0, setup_function22);
+ RESET_ENTITY_STATE(kEntityServers1, Servers1, setup_function16);
+ RESET_ENTITY_STATE(kEntityCooks, Cooks, setup_function7);
+
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_function42);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_chapter1Handler);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_chapter1Handler);
+
+ getSavePoints()->push(kEntityChapters, kEntityMertens, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ RESET_ENTITY_STATE(kEntityKronos, Kronos, setup_function10);
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function13);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function34);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function34);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function24);
+ RESET_ENTITY_STATE(kEntityVassili, Vassili, setup_function7);
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function26);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_function18);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_function15);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_function17);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function11);
+ RESET_ENTITY_STATE(kEntityFrancois, Francois, setup_function20);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_function16);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_function22);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function27);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_function5);
+ RESET_ENTITY_STATE(kEntityMahmud, Mahmud, setup_resetChapter);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_function10);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_function12);
+ RESET_ENTITY_STATE(kEntityHadija, Alouan, setup_function12);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->processEntries();
+
+ if (getSound()->isBuffered("CON1505"))
+ getSound()->processEntry("CON1505");
+
+ if (getSound()->isBuffered("AUG1057"))
+ getSound()->processEntry("AUG1057");
+
+ if (getSound()->isBuffered("ZFX1005"))
+ getSound()->processEntry("ZFX1005");
+
+ if (getSound()->isBuffered("ZFX1006"))
+ getSound()->processEntry("ZFX1006");
+
+ if (getSound()->isBuffered("ZFX1007"))
+ getSound()->processEntry("ZFX1007");
+
+ if (getSound()->isBuffered("ZFX1007A"))
+ getSound()->processEntry("ZFX1007A");
+
+ if (getSound()->isBuffered("ZFX1007B"))
+ getSound()->processEntry("ZFX1007B");
+
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ // FIXME add event pump ?
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ getProgress().field_84 = true;
+
+ getScenes()->loadSceneFromPosition(kCarLocomotive, 75);
+ getInventory()->show();
+
+ getState()->time = kTime1492200;
+ getProgress().field_18 = 4;
+ getState()->timeDelta = 0;
+
+ getObjects()->update(kObject63, kEntityChapters, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ getProgress().isTrainRunning = false;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kAction225358684:
+ params->param2++;
+
+ if (params->param2 >= 3) {
+
+ if (!getSound()->isBuffered("LIB031", true))
+ getSound()->playSound(kEntityPlayer, "LIB031");
+
+ if (params->param2 == 3) {
+ getData()->car = kCarGreenSleeping;
+ getEntities()->drawSequenceLeft(kEntityChapters, "JUGL");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Chapters, chapter1Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getProgress().chapter = kChapter1;
+ getSound()->resetState();
+
+ getState()->time = kTimeChapter1;
+ getState()->timeDelta = 0;
+ getProgress().isTrainRunning = true;
+ getProgress().portrait = kPortraitOriginal;
+ getProgress().field_18 = 1;
+
+ // Setup inventory & items location
+ getInventory()->addItem(kItemTelegram);
+ getInventory()->addItem(kItemArticle);
+
+ getInventory()->setLocationAndProcess(kItemScarf, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemParchemin, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemGreenJacket, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemCorpse, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemPassengerList, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem22, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemPaper, kObjectLocation1);
+
+ getProgress().field_7C = 1;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ for (uint i = kObjectCompartment1; i <= kObjectCompartment8; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ for (uint i = kObjectCompartmentA; i <= kObjectCompartmentH; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ params->param1 = 40;
+
+ getObjects()->updateLocation2(kObject25, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectTrainTimeTable, kObjectLocation1);
+ getObjects()->updateLocation2(kObject98, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation1);
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectRedSleepingCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject28, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject56, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject54, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject59, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject66, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject64, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject69, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject98, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHandKnock);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHandKnock);
+ getObjects()->update(kObject101, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_chapter1Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+#define PLAY_STEAM() { \
+ getSound()->resetState(); \
+ getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); \
+ ENTITY_PARAM(0, 2) = 0; \
+ }
+
+IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().isTrainRunning || getState()->time >= kTime1458000)
+ goto label_processStations;
+
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param2)
+ // Play sound FX
+ getSound()->playLocomotiveSound();
+
+ params->param2 = 225 * (4 * rnd(5) + 20);
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+
+label_processStations:
+ // Process stations
+ TIME_CHECK_CALLBACK_2(kTime1039500, params->param7, 1, setup_savegame, kSavegameTypeTime, kTimeNone);
+
+label_enter_epernay:
+ // Entering Epernay station
+ TIME_CHECK_CALLBACK_2(kTimeEnterEpernay, params->param8, 1, setup_enterStation, "Epernay", kCityEpernay);
+
+label_exit_epernay:
+ // Exiting Epernay station
+ if (getState()->time > kTimeExitEpernay && !CURRENT_PARAM(1, 1)) {
+ CURRENT_PARAM(1, 1) = 1;
+ params->param4 = 1;
+ setCallback(3);
+ setup_exitStation("Epernay");
+ break;
+ }
+
+label_epernay_police:
+ if (params->param5 && !ENTITY_PARAM(0, 2)) {
+ setCallback(4);
+ setup_exitStation("Unschedu");
+ break;
+ }
+
+label_enter_chalons:
+ if (getState()->time > kTimeEnterChalons && !CURRENT_PARAM(1, 2)) {
+ CURRENT_PARAM(1, 2) = 1;
+ getProgress().field_18 = 2;
+ }
+
+ // Skip to callback 18 checks
+ if (params->param1)
+ goto label_exit_strasbourg;
+
+ // Entering Chalons station
+ TIME_CHECK_CALLBACK_2(kTimeEnterChalons, CURRENT_PARAM(1, 3), 5, setup_enterStation, "Chalons", kCityChalons);
+
+label_exit_chalons:
+ // Exiting Chalons station
+ TIME_CHECK_CALLBACK_1(kTimeExitChalons, CURRENT_PARAM(1, 4), 6, setup_exitStation, "Chalons");
+
+label_enter_barleduc:
+ // Entering Bar-Le-Duc station
+ TIME_CHECK_CALLBACK_2(kTimeCityBarLeDuc, CURRENT_PARAM(1, 5), 7, setup_enterStation, "BarLeDuc", kCityBarleduc);
+
+label_exit_barleduc:
+ // Exiting Bar-Le-Duc station
+ TIME_CHECK_CALLBACK_1(kTimeExitBarLeDuc, CURRENT_PARAM(1, 6), 8, setup_exitStation, "BarLeDuc");
+
+label_enter_nancy:
+ if (getState()->time > kTime1260000 && !CURRENT_PARAM(1, 7)) {
+ CURRENT_PARAM(1, 7) = 1;
+ getState()->timeDelta = 1;
+ }
+
+ // Entering Nancy station
+ TIME_CHECK_CALLBACK_2(kTimeCityNancy, CURRENT_PARAM(1, 8), 9, setup_enterStation, "Nancy", kCityNancy);
+
+label_exit_nancy:
+ // Exiting Nancy station
+ TIME_CHECK_CALLBACK_1(kTimeExitNancy, CURRENT_PARAM(2, 1), 10, setup_exitStation, "Nancy");
+
+label_enter_luneville:
+ // Entering Luneville station
+ TIME_CHECK_CALLBACK_2(kTimeCityLuneville, CURRENT_PARAM(2, 2), 11, setup_enterStation, "Luneville", kCityLuneville);
+
+label_exit_luneville:
+ // Exiting Luneville station
+ TIME_CHECK_CALLBACK_1(kTimeExitLuneville, CURRENT_PARAM(2, 3), 12, setup_exitStation, "Luneville");
+
+label_enter_avricourt:
+ // Entering Avricourt station
+ TIME_CHECK_CALLBACK_2(kTimeCityAvricourt, CURRENT_PARAM(2, 4), 13, setup_enterStation, "Avricourt", kCityAvricourt);
+
+label_exit_avricourt:
+ // Exiting Avricourt station
+ TIME_CHECK_CALLBACK_1(kTimeExitAvricourt, CURRENT_PARAM(2, 5), 14, setup_exitStation, "Avricourt");
+
+label_enter_deutschavricourt:
+ // Entering Deutsch-Avricourt station
+ TIME_CHECK_CALLBACK_2(kTimeCityDeutschAvricourt, CURRENT_PARAM(2, 6), 15, setup_enterStation, "DeutschA", kCityDeutschAvricourt);
+
+label_exit_deutschavricourt:
+ // Exiting Avricourt station
+ TIME_CHECK_CALLBACK_1(kTimeExitDeutschAvricourt, CURRENT_PARAM(2, 7), 16, setup_exitStation, "DeutschA");
+
+label_enter_strasbourg:
+ TIME_CHECK_CALLBACK_2(kTimeCityStrasbourg, CURRENT_PARAM(2, 8), 17, setup_savegame, kSavegameTypeTime, kTimeNone);
+
+label_exit_strasbourg:
+ // Exiting Strasbourg station
+ TIME_CHECK_CALLBACK_1(kTimeExitStrasbourg, CURRENT_PARAM(3, 1), 19, setup_exitStation, "Strasbou");
+
+label_enter_badenoos:
+ // Entering Baden Oos station
+ TIME_CHECK_CALLBACK_2(kTimeCityBadenOos, CURRENT_PARAM(3, 2), 20, setup_enterStation, "BadenOos", kCityBadenOos);
+
+label_exit_badenoos:
+ // Exiting Baden Oos station
+ TIME_CHECK_CALLBACK_1(kTimeExitBadenOos, CURRENT_PARAM(3, 3), 21, setup_exitStation, "BadenOos");
+
+label_chapter1_next:
+ if (getState()->time > kTimeChapter1End3 && ! CURRENT_PARAM(3, 4)) {
+ CURRENT_PARAM(3, 4) = 1;
+ setup_chapter1Next();
+ }
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car < kCarBaggageRear || car > kCarGreenSleeping) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+
+ if (params->param4) {
+ getSavePoints()->push(kEntityChapters, getProgress().field_24 ? kEntityVerges : kEntityMertens, getProgress().field_24 ? kAction168187490 : kAction224122407);
+ params->param4 = 0;
+ }
+ }
+ break;
+
+ case kActionDefault:
+ params->param2 = 225 * (4 * rnd(5) + 20);
+ break;
+
+ case kActionDrawScene:
+ if (!params->param3) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ getState()->time = kTimeChapter1;
+ getState()->timeDelta = 3;
+ params->param3 = 1;
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_enter_epernay;
+
+ case 2:
+ goto label_exit_epernay;
+
+ case 3:
+ goto label_epernay_police;
+
+ case 4:
+ params->param5 = 0;
+ goto label_enter_chalons;
+
+ case 5:
+ goto label_exit_chalons;
+
+ case 6:
+ goto label_enter_barleduc;
+
+ case 7:
+ goto label_exit_barleduc;
+
+ case 8:
+ goto label_enter_nancy;
+
+ case 9:
+ goto label_exit_nancy;
+
+ case 10:
+ goto label_enter_luneville;
+
+ case 11:
+ goto label_exit_luneville;
+
+ case 12:
+ goto label_enter_avricourt;
+
+ case 13:
+ goto label_exit_avricourt;
+
+ case 14:
+ goto label_enter_deutschavricourt;
+
+ case 15:
+ goto label_exit_deutschavricourt;
+
+ case 16:
+ getState()->time = kTimeEnterStrasbourg;
+ goto label_enter_strasbourg;
+
+ case 17:
+ getProgress().field_18 = 1;
+ setCallback(18);
+ setup_enterStation("Strasbou", kCityStrasbourg);
+ break;
+
+ case 18:
+ goto label_exit_strasbourg;
+
+ case 19:
+ getState()->timeDelta = 1;
+ goto label_enter_badenoos;
+
+ case 20:
+ goto label_exit_badenoos;
+
+ case 21:
+ goto label_chapter1_next;
+
+ case 22:
+ params->param5 = 1;
+ break;
+
+ case 23:
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction169629818:
+ setCallback(22);
+ setup_enterStation("Unschedu", kCityPolice);
+ break;
+
+ case kActionEndChapter:
+ getProgress().field_18 = 3;
+
+ if (getState()->time >= kTimeChapter1End) {
+ setup_chapter1Next();
+ } else {
+ setCallback(23);
+ setup_chapter1End();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Chapters, chapter1Next)
+ if (savepoint.action == kActionDefault) {
+ // Reset sound cache
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ setup_chapter2();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Chapters, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Setup for chapter 2 in case it hasn't been done before
+ if (getProgress().chapter != kChapter2) {
+ getProgress().chapter = kChapter2;
+ getEntities()->setupChapter(kChapter2);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter2;
+ getState()->timeDelta = 5;
+
+ // Save game
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!_engine->getResourceManager()->loadArchive(kArchiveCd2)) {
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+ return;
+ }
+
+ // Load scene data
+ getScenes()->loadSceneDataFile(kArchiveCd2);
+ setup_chapter2Init();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Chapters, chapter2Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getProgress().eventCorpseMovedFromFloor = true;
+ getProgress().field_18 = 1;
+ getProgress().isTrainRunning = true;
+ getProgress().eventCorpseFound = true;
+
+ // Switch to green jacket/portrait
+ getProgress().jacket = kJacketGreen;
+ getProgress().portrait = kPortraitGreen;
+
+ // Setup inventory & items location
+ getInventory()->addItem(kItemGreenJacket);
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getInventory()->setLocationAndProcess(kItemBeetle, kObjectLocation3);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ for (uint i = 1; i < 9; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ for (uint i = 33; i < 40; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ params->param1 = 40;
+
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ // Reset sound cache
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getAction()->playAnimation(kEventTrainPassing);
+
+ if (getInventory()->hasItem(kItemScarf))
+ getScenes()->loadScene(kScene41);
+ else
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 79);
+
+ setup_chapter2Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Chapters, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().isTrainRunning)
+ break;
+
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1);
+
+ getSound()->playLocomotiveSound();
+
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ break;
+
+ case kActionChapter3:
+ setup_chapter3();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Chapters, chapter3)
+ if (savepoint.action == kActionDefault) {
+ // Setup for chapter 3 in case it hasn't been done before
+ if (getProgress().chapter != kChapter3) {
+ getProgress().chapter = kChapter3;
+ getEntities()->setupChapter(kChapter3);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter3;
+ getState()->timeDelta = 5;
+
+ setup_chapter3Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Chapters, chapter3Init)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables5, kActionDrawTablesWithChairs);
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment1, kObjectLocation2);
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation3, kCursorKeepValue, kCursorKeepValue);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 60);
+ getInventory()->show();
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter3Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().isTrainRunning) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, params->param1)
+ getSound()->playLocomotiveSound();
+
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param4 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, params->param2)
+ switch (rnd(2)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityPlayer, "ZFX1008", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "ZFX1009", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+ }
+
+ params->param2 = 225 * (4 * rnd(6) + 8);
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_2(kTimeEnterSalzbourg, params->param6, 1, setup_enterStation, "Salzburg", kCitySalzbourg);
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTimeExitSalzbourg, params->param7, 2, setup_exitStation, "Salzburg");
+
+label_callback_2:
+ TIME_CHECK_CALLBACK_2(kTimeEnterAttnangPuchheim, params->param8, 3, setup_enterStation, "Attnang", kCityAttnangPuchheim);
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTimeExitAttnangPuchheim, CURRENT_PARAM(1, 1), 4, setup_exitStation, "Attnang");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_2(kTimeEnterWels, CURRENT_PARAM(1, 2), 5, setup_enterStation, "Wels", kCityWels);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK_1(kTimeEnterWels, CURRENT_PARAM(1, 3), 6, setup_exitStation, "Wels");
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_2(kTimeEnterLinz, CURRENT_PARAM(1, 4), 7, setup_enterStation, "Linz", kCityLinz);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTimeCityLinz, CURRENT_PARAM(1, 5), 8, setup_exitStation, "Linz");
+
+label_callback_8:
+ if (getState()->time > kTime2187000 && !CURRENT_PARAM(1, 6)) {
+ CURRENT_PARAM(1, 6) = 1;
+ getState()->timeDelta = 5;
+ }
+
+ TIME_CHECK_CALLBACK_2(kTimeCityVienna, CURRENT_PARAM(1, 7), 9, setup_enterStation, "Vienna", kCityVienna);
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location == kLocationOutsideTrain) {
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ } else if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ } else {
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car >= kCarBaggageRear && car <= kCarGreenSleeping) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ }
+ }
+ } else {
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ }
+ }
+ }
+
+ getSound()->resetState();
+ getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4));
+
+ ENTITY_PARAM(0, 2) = 0;
+ if (params->param1)
+ setup_viennaEvents();
+
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param2 = 225 * (4 * rnd(6) + 8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ params->param3 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Chapters, viennaEvents)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventViennaAugustUnloadGuns);
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ getLogic()->gameOver(kSavegameTypeTime, kTime2187000, kSceneNone, true);
+ else if (getEvent(kEventCathJumpDownCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent, kEventCathJumpDownCeiling, kSceneNone, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventViennaKronosFirebird);
+ if (getEvent(kEventKronosBringEggCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventKronosBringEggCeiling, kSceneGameOverVienna1, true);
+ else if (getEvent(kEventKronosBringEgg)) {
+ if (getEvent(kEventKronosBringEggCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventKronosBringEggCeiling, kSceneGameOverVienna1, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna1, true);
+ } else {
+ if (getProgress().field_C0) {
+ if (getEvent(kEventKronosReturnBriefcase))
+ getLogic()->gameOver(kSavegameTypeTime, getProgress().field_C0, kSceneGameOverVienna2, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna2, true);
+ } else {
+ if (getEvent(kEventKronosReturnBriefcase))
+ getLogic()->gameOver(kSavegameTypeEvent, kEventKronosReturnBriefcase, kSceneGameOverVienna, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna, true);
+ }
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventVergesAnnaDead);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2250000, kSceneGameOverAnnaDied, true);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventViennaContinueGame);
+ setup_chapter4();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Chapters, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Setup for chapter 4 in case it hasn't been done before
+ if (getProgress().chapter != kChapter4) {
+ getProgress().chapter = kChapter4;
+ getEntities()->setupChapter(kChapter4);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter4;
+ getState()->timeDelta = 5;
+
+ // Save game
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!_engine->getResourceManager()->loadArchive(kArchiveCd3)) {
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+ return;
+ }
+
+ // Load scene data
+ getScenes()->loadSceneDataFile(kArchiveCd3);
+ setup_chapter4Init();
+ }
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Chapters, chapter4Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getSound()->processEntries();
+ getSound()->resetState();
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStartRunning);
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables5, kActionDrawTablesWithChairs);
+
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ getInventory()->setLocationAndProcess(kItemBomb, kObjectLocation1);
+
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3)
+ getScenes()->loadSceneFromItemPosition(kItemBeetle);
+
+ getObjects()->updateLocation2(kObject25, kObjectLocation2);
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation3, kCursorKeepValue, kCursorKeepValue);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 76);
+ else
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+
+ getInventory()->show();
+ setup_chapter4Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().isTrainRunning) {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param4);
+ getSound()->playLocomotiveSound();
+
+ params->param4 = 225 * (4 * rnd(5) + 20);
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, params->param5)
+ switch (rnd(2)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityPlayer, "ZFX1008", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "ZFX1009", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+ }
+
+ params->param5 = 225 * (4 * rnd(6) + 8);
+ params->param7 = 0;
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_2(kTimeEnterPoszony, params->param8, 1, setup_enterStation, "Pozsony", kCityPoszony);
+
+label_exitPozsony:
+ TIME_CHECK_CALLBACK_1(kTimeExitPoszony, CURRENT_PARAM(1, 1), 2, setup_exitStation, "Pozsony");
+
+label_enterGalanta:
+ if (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) {
+ if (getState()->time > kTime2403000 && !CURRENT_PARAM(1, 2)) {
+ CURRENT_PARAM(1, 2) = 1;
+ getProgress().field_18 = 2;
+ }
+ }
+
+ if (params->param1)
+ goto label_callback_4;
+
+ TIME_CHECK_CALLBACK_2(kTimeEnterGalanta, CURRENT_PARAM(1, 3), 3, setup_enterStation, "Galanta", kCityGalanta);
+
+label_exitGalanta:
+ TIME_CHECK_CALLBACK_1(kTimeExitGalanta, CURRENT_PARAM(1, 4), 4, setup_exitStation, "Galanta");
+
+label_callback_4:
+ if (getState()->time > kTime2470500 && !CURRENT_PARAM(1, 5)) {
+ CURRENT_PARAM(1, 5) = 1;
+
+ if (getProgress().field_18 == 2)
+ getState()->timeDelta = 1;
+ }
+
+ if (getState()->time > kTime2506500 && !CURRENT_PARAM(1, 6)) {
+ CURRENT_PARAM(1, 6) = 1;
+
+ if (getProgress().field_18 == 2)
+ getProgress().field_18 = 1;
+ }
+
+ if (getState()->time > kTime2520000 && !CURRENT_PARAM(1, 7)) {
+ CURRENT_PARAM(1, 7) = 1;
+
+ if (!params->param2 && !params->param3) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventTrainExplosionBridge);
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car < kCarBaggageRear || car > kCarGreenSleeping) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+ } else if (!params->param2 && !params->param3) {
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ }
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntityChapters);
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionDefault:
+ params->param4 = 225 * (4 * rnd(5) + 20);
+ params->param5 = 225 * (4 * rnd(6) + 8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_exitPozsony;
+
+ case 2:
+ goto label_enterGalanta;
+
+ case 3:
+ goto label_exitGalanta;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getAction()->playAnimation(kEventTrainExplosionBridge);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 6:
+ getSound()->processEntries();
+ getAction()->playAnimation(kEventTylerCastleDream);
+ getSound()->resetState();
+
+ getProgress().field_18 = 1;
+
+ getScenes()->loadScene(kScene41);
+ getSavePoints()->push(kEntityChapters, kEntityTatiana, kAction169360385);
+
+ getState()->timeDelta = 1;
+ getState()->time = kTime2511900;
+
+ getInventory()->setLocationAndProcess(kItem2, kObjectLocation1);
+ getScenes()->loadSceneFromItemPosition(kItem22);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventTrainExplosionBridge);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2430000, kSceneNone, true);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityPlayer, "MUS022");
+
+ if (getState()->time < kTime2517300)
+ getState()->time = kTime2517300;
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventCathDefusingBomb);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 73);
+ break;
+
+ case 10:
+ getAction()->playAnimation(kEventDefuseBomb);
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_function48);
+ getSavePoints()->push(kEntityChapters, kEntityAnna, kAction191001984);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction191001984);
+ getScenes()->loadSceneFromItemPosition(kItem2);
+
+ getInventory()->get(kItem2)->location = kObjectLocationNone;
+ params->param2 = 1;
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 2);
+ break;
+
+ case 11:
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 74);
+ getSound()->playSound(kEntityTrain, "ZFX4001", SoundManager::kFlagDefault);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+ break;
+
+ case kActionChapter5:
+ setup_chapter5();
+ break;
+
+ case kAction156435676:
+ getSavePoints()->push(kEntityChapters, kEntityTatiana, kAction169360385);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ getState()->timeDelta = 1;
+ getState()->time = kTime2511900;
+
+ getInventory()->setLocationAndProcess(kItem2, kObjectLocation1);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ break;
+
+ case kAction158610240:
+ setCallback(8);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kAction169300225:
+ if (getState()->time < kTime2519100)
+ getState()->time = kTime2519100;
+
+ params->param3 = 1;
+
+ getEntities()->drawSequenceRight(kEntityChapters, "BOMB");
+ break;
+
+ case kAction190346110:
+ getProgress().field_18 = 3;
+
+ params->param1 = 1;
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ if (getInventory()->hasItem(kItemBomb)) {
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function47);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function68);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function65);
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_function48);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function53);
+ RESET_ENTITY_STATE(kEntityServers0, Servers0, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityServers1, Servers1, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityPascale, Pascale, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function49);
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_function44);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_function32);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_function27);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_function29);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function22);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_function25);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_function35);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function45);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_function9);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_function17);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_function19);
+ RESET_ENTITY_STATE(kEntityAlouan, Alouan, setup_function19);
+ RESET_ENTITY_STATE(kEntityMax, Max, setup_chapter4Handler);
+ getSavePoints()->push(kEntityChapters, kEntityAnna, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityMertens, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityServers0, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityServers1, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityPascale, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventTylerCastleDream);
+ } else {
+ getState()->time = kTime2520000;
+
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventTrainExplosionBridge);
+ }
+ break;
+
+ case kAction191001984:
+ getState()->time = kTime2520000;
+
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getEntities()->clearSequences(kEntityChapters);
+ getInventory()->removeItem(kItemTelegram);
+
+ getState()->timeDelta = 5;
+
+ setCallback(10);
+ setup_savegame(kSavegameTypeEvent, kEventDefuseBomb);
+ break;
+
+ case kAction201959744:
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getSound()->playSound(kEntityTrain, "ZFX4001", SoundManager::kFlagDefault);
+
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ break;
+
+ case kAction225367984:
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventCathDefusingBomb);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Chapters, chapter5)
+ if (savepoint.action == kActionDefault) {
+ // Setup for chapter 5 in case it hasn't been done before
+ if (getProgress().chapter != kChapter5) {
+ getProgress().chapter = kChapter5;
+ getEntities()->setupChapter(kChapter5);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter5;
+ getState()->timeDelta = 2;
+
+ setup_chapter5Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Chapters, chapter5Init)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTables0);
+ getEntities()->clearSequences(kEntityTables1);
+ getEntities()->clearSequences(kEntityTables2);
+ getEntities()->clearSequences(kEntityTables3);
+ getEntities()->clearSequences(kEntityTables4);
+ getEntities()->clearSequences(kEntityTables5);
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectKitchen, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject20, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject21, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject22, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_18 = 1;
+ getProgress().field_84 = 1;
+ getProgress().portrait = kCursorPortraitYellow;
+
+ getInventory()->unselectItem();
+
+ getInventory()->removeItem(kItemKey);
+ getInventory()->removeItem(kItemBomb);
+ getInventory()->removeItem(kItemMatch);
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->setLocationAndProcess(kItemFirebird, kObjectLocation3);
+
+ if (getInventory()->hasItem(kItemWhistle)) {
+ getInventory()->removeItem(kItemWhistle);
+ getInventory()->setLocationAndProcess(kItemWhistle, kObjectLocation3);
+ }
+ }
+
+ getObjects()->update(kObject93, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject94, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject101, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getObjects()->updateLocation2(kObject98, kObjectLocation2);
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation2);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 95);
+ getInventory()->show();
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter5Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Chapters, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2907000 && !params->param2) {
+ params->param2 = 1;
+
+ if (!getProgress().isNightTime) {
+ getSound()->playSound(kEntityChapters, "ARRIVE", SoundManager::kFlag8);
+ getSound()->processEntries();
+ }
+ }
+
+ if (getState()->time > kTimeTrainStopped2 && !params->param3) {
+ params->param3 = 1;
+
+ if (!getEvent(kEventLocomotiveMilosDay) && !getEvent(kEventLocomotiveMilosNight)) {
+ getSound()->playSound(kEntityChapters, "ARRIVE", SoundManager::kFlag8);
+ getSound()->processEntries();
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ if (getState()->time <= kTimeTrainStopped2) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTrainStopped);
+ } else {
+ getLogic()->gameOver(kSavegameTypeTime, kTimeTrainStopped2, kSceneGameOverTrainStopped, true);
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(10) + 20);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventTrainStopped);
+ getLogic()->gameOver(kSavegameTypeTime, kTimeTrainStopped, kSceneGameOverTrainStopped, true);
+ }
+ break;
+
+ case kAction135800432:
+ getProgress().isNightTime = true;
+ getState()->time = kTime2916000;
+
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Chapters::enterExitStation(const SavePoint &savepoint, bool isEnteringStation) {
+ if (savepoint.action == kActionDefault) {
+ if (!ENTITY_PARAM(0, 2) && !ENTITY_PARAM(0, 3)) {
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getSound()->removeFromQueue(kEntityChapters);
+
+ if (!ENTITY_PARAM(0, 2)) {
+ if (ENTITY_PARAM(0, 3))
+ ENTITY_PARAM(0, 3) = 0;
+
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Green sleeping car
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Red sleeping car
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Other cars
+ if (getEntityData(kEntityPlayer)->car < kCarRedSleeping || getEntityData(kEntityPlayer)->car > kCarCoalTender) {
+
+ if (getEntityData(kEntityPlayer)->car < kCarBaggageRear || getEntityData(kEntityPlayer)->car > kCarGreenSleeping) {
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Chapters::enterExitHelper(bool isEnteringStation) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ getSound()->playSound(kEntityChapters, isEnteringStation ? "ARRIVE" : "DEPART", SoundManager::kFlag8);
+ getSound()->processEntries();
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, isEnteringStation ? kCursorNormal : kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, isEnteringStation ? kCursorNormal : kCursorHand);
+
+ getProgress().isTrainRunning = isEnteringStation ? false : true;
+
+ if (isEnteringStation) {
+ ENTITY_PARAM(0, 2) = 1;
+ ENTITY_PARAM(0, 4) = params->param4;
+ } else {
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStartRunning);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ CALLBACK_ACTION();
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h
new file mode 100644
index 0000000000..6a31727c23
--- /dev/null
+++ b/engines/lastexpress/entities/chapters.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_CHAPTERS_H
+#define LASTEXPRESS_CHAPTERS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Chapters : public Entity {
+public:
+ Chapters(LastExpressEngine *engine);
+ ~Chapters() {}
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Exit a train station
+ *
+ * @param stationName The name of the train station
+ * @param index The index of the train station
+ */
+ DECLARE_FUNCTION_2(enterStation, const char *stationName, CityIndex index)
+
+ /**
+ * Exit a train station
+ *
+ * @param stationName The name of the train station
+ */
+ DECLARE_FUNCTION_1(exitStation, const char *stationName)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Reset main entities
+ */
+ DECLARE_FUNCTION(resetMainEntities)
+
+ /**
+ * Handle end of Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1End)
+
+ /**
+ * Init Chapter 1 data
+ */
+ DECLARE_FUNCTION(chapter1Init)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ /**
+ * Handle switching to Chapter 2 after the end of Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1Next)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Init Chapter 2 data
+ */
+ DECLARE_FUNCTION(chapter2Init)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Init Chapter 3 data
+ */
+ DECLARE_FUNCTION(chapter3Init)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Handle Vienna events
+ */
+ DECLARE_FUNCTION(viennaEvents)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Init Chapter 4 data
+ */
+ DECLARE_FUNCTION(chapter4Init)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Init Chapter 5 data
+ */
+ DECLARE_FUNCTION(chapter5Init)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+private:
+ void enterExitStation(const SavePoint &savepoint, bool isEnteringStation);
+ void enterExitHelper(bool isEnteringStation);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_CHAPTERS_H
diff --git a/engines/lastexpress/entities/cooks.cpp b/engines/lastexpress/entities/cooks.cpp
new file mode 100644
index 0000000000..51e723887b
--- /dev/null
+++ b/engines/lastexpress/entities/cooks.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/cooks.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Cooks::Cooks(LastExpressEngine *engine) : Entity(engine, kEntityCooks) {
+ ADD_CALLBACK_FUNCTION(Cooks, draw);
+ ADD_CALLBACK_FUNCTION(Cooks, playSound);
+ ADD_CALLBACK_FUNCTION(Cooks, function3);
+ ADD_CALLBACK_FUNCTION(Cooks, function4);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter1);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, function7);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter2);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter3);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter4);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(1, Cooks, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Cooks, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Cooks, function3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308A");
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+
+ switch (getProgress().chapter) {
+ default:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case kChapter1:
+ setCallback(1);
+ setup_playSound("KIT1010");
+ break;
+
+ case kChapter3:
+ setCallback(2);
+ setup_playSound("KIT1012");
+ break;
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer)) {
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 46)) {
+ getEntities()->drawSequenceLeft(kEntityCooks, "308D");
+
+ if (!getSound()->isBuffered(kEntityCooks)) {
+ if (params->param1) {
+ if (!getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ // Kitchen apprentice getting a lesson :D
+ getSound()->playSound(kEntityCooks, "KIT1011A");
+ params->param1 = 1;
+ }
+ }
+
+ if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308C");
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 78);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Cooks, function4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308A");
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ break;
+
+ case kChapter3:
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ break;
+ }
+
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer)) {
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 80)) {
+ getEntities()->drawSequenceLeft(kEntityCooks, "308D");
+
+ if (!getSound()->isBuffered(kEntityCooks)) {
+ if (params->param1) {
+ if (!getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ // Kitchen apprentice getting a lesson :D
+ getSound()->playSound(kEntityCooks, "KIT1011A");
+ params->param1 = 1;
+ }
+ }
+
+ if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308C");
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Cooks, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getProgress().field_4C = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Cooks, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param4, getState()->time, params->param2);
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ params->param4 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ if (params->param1) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 73)) {
+ setCallback(1);
+ setup_function3();
+ }
+ } else {
+ if (params->param3) {
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(3);
+ setup_playSound("ZFX1012");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ break;
+
+ case 2:
+ case 3:
+ params->param3 = !params->param3;
+ break;
+ }
+ break;
+
+ case kAction101632192:
+ setup_function7();
+ break;
+
+ case kAction224849280:
+ getProgress().field_4C = 1;
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Cooks, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Snoring...
+ setCallback(1);
+ setup_playSound("WAT1200");
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityCooks);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Cooks, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 1;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Cooks, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param1);
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2) {
+ setCallback(1);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(2);
+ setup_playSound("ZFX1012");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2)
+ params->param2 = !params->param2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Cooks, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Cooks, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param4, getState()->time, params->param2)
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ params->param4 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (getState()->time > kTime2079000 && !params->param5) {
+ params->param1 = 0;
+ params->param5 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ if (params->param1) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 80)) {
+ setCallback(1);
+ setup_function4();
+ }
+ } else {
+ if (params->param3) {
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(3);
+ setup_playSound("ZFX1012");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ break;
+
+ case 2:
+ case 3:
+ params->param3 = !params->param3;
+ break;
+ }
+ break;
+
+ case kAction236976550:
+ getProgress().field_4C = 1;
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Cooks, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 1;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Cooks, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param1)
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ // Kitchen background sound
+ if (params->param2) {
+ setCallback(1);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(2);
+ setup_playSound("ZFX1012");
+ }
+ break;
+
+
+ case kActionCallback:
+ // Play the next part of background sound
+ if (getCallback() == 1 || getCallback() == 2) {
+ params->param2 = !params->param2;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Cooks, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityCooks);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/cooks.h b/engines/lastexpress/entities/cooks.h
new file mode 100644
index 0000000000..fc3a0cb78c
--- /dev/null
+++ b/engines/lastexpress/entities/cooks.h
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_COOKS_H
+#define LASTEXPRESS_COOKS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Cooks : public Entity {
+public:
+ Cooks(LastExpressEngine *engine);
+ ~Cooks() {}
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ DECLARE_FUNCTION(function3)
+
+ DECLARE_FUNCTION(function4)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_COOKS_H
diff --git a/engines/lastexpress/entities/coudert.cpp b/engines/lastexpress/entities/coudert.cpp
new file mode 100644
index 0000000000..6dece39161
--- /dev/null
+++ b/engines/lastexpress/entities/coudert.cpp
@@ -0,0 +1,4179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+#define SAVEGAME_BLOOD_JACKET() \
+ if (getProgress().jacket == kJacketBlood \
+ && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000) \
+ && !getEntities()->isInsideCompartments(kEntityPlayer) \
+ && !getEntities()->checkFields10(kEntityPlayer)) { \
+ setCallback(1); \
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \
+ }
+
+Coudert::Coudert(LastExpressEngine *engine) : Entity(engine, kEntityCoudert) {
+ ADD_CALLBACK_FUNCTION(Coudert, reset);
+ ADD_CALLBACK_FUNCTION(Coudert, bloodJacket);
+ ADD_CALLBACK_FUNCTION(Coudert, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Coudert, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Coudert, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Coudert, playSound);
+ ADD_CALLBACK_FUNCTION(Coudert, playSound16);
+ ADD_CALLBACK_FUNCTION(Coudert, savegame);
+ ADD_CALLBACK_FUNCTION(Coudert, updateEntity);
+ ADD_CALLBACK_FUNCTION(Coudert, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Coudert, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Coudert, excuseMe);
+ ADD_CALLBACK_FUNCTION(Coudert, function13);
+ ADD_CALLBACK_FUNCTION(Coudert, function14);
+ ADD_CALLBACK_FUNCTION(Coudert, function15);
+ ADD_CALLBACK_FUNCTION(Coudert, function16);
+ ADD_CALLBACK_FUNCTION(Coudert, function17);
+ ADD_CALLBACK_FUNCTION(Coudert, function18);
+ ADD_CALLBACK_FUNCTION(Coudert, function19);
+ ADD_CALLBACK_FUNCTION(Coudert, function20);
+ ADD_CALLBACK_FUNCTION(Coudert, function21);
+ ADD_CALLBACK_FUNCTION(Coudert, function22);
+ ADD_CALLBACK_FUNCTION(Coudert, function23);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentF);
+ ADD_CALLBACK_FUNCTION(Coudert, function25);
+ ADD_CALLBACK_FUNCTION(Coudert, function26);
+ ADD_CALLBACK_FUNCTION(Coudert, function27);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentB);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentA);
+ ADD_CALLBACK_FUNCTION(Coudert, function30);
+ ADD_CALLBACK_FUNCTION(Coudert, function31);
+ ADD_CALLBACK_FUNCTION(Coudert, function32);
+ ADD_CALLBACK_FUNCTION(Coudert, function33);
+ ADD_CALLBACK_FUNCTION(Coudert, function34);
+ ADD_CALLBACK_FUNCTION(Coudert, function35);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter1);
+ ADD_CALLBACK_FUNCTION(Coudert, function37);
+ ADD_CALLBACK_FUNCTION(Coudert, function38);
+ ADD_CALLBACK_FUNCTION(Coudert, function39);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Coudert, function41);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter2);
+ ADD_CALLBACK_FUNCTION(Coudert, function43);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter3);
+ ADD_CALLBACK_FUNCTION(Coudert, function45);
+ ADD_CALLBACK_FUNCTION(Coudert, function46);
+ ADD_CALLBACK_FUNCTION(Coudert, function47);
+ ADD_CALLBACK_FUNCTION(Coudert, function48);
+ ADD_CALLBACK_FUNCTION(Coudert, function49);
+ ADD_CALLBACK_FUNCTION(Coudert, function50);
+ ADD_CALLBACK_FUNCTION(Coudert, function51);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter4);
+ ADD_CALLBACK_FUNCTION(Coudert, function53);
+ ADD_CALLBACK_FUNCTION(Coudert, function54);
+ ADD_CALLBACK_FUNCTION(Coudert, function55);
+ ADD_CALLBACK_FUNCTION(Coudert, function56);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter5);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Coudert, function59);
+ ADD_CALLBACK_FUNCTION(Coudert, function60);
+ ADD_CALLBACK_FUNCTION(Coudert, function61);
+ ADD_CALLBACK_FUNCTION(Coudert, function62);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Coudert, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Coudert, bloodJacket)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Coudert, enterExitCompartment, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Coudert, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(5, Coudert, enterExitCompartment2, ObjectIndex, EntityPosition, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint, (EntityPosition)params->param5, (EntityPosition)params->param6, kCarRedSleeping, (ObjectIndex)params->param4, false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Coudert, playSound)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Coudert, playSound16)
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityCoudert, (char *)&params->seq1, SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Coudert, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getProgress().jacket != kJacketBlood
+ || !getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000)
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields10(kEntityPlayer)) {
+ if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+
+ case kAction1:
+ params->param3 = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getData()->clothes == kClothes1)
+ getSound()->playSound(kEntityPlayer, "ZFX1003", getSound()->getSoundFlag(kEntityCoudert));
+ else if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityPlayer, "JAC1112", getSound()->getSoundFlag(kEntityCoudert));
+ break;
+
+ case kActionExcuseMe:
+ if (getData()->clothes == kClothes1)
+ getSound()->playSound(kEntityPlayer, "ZFX1003", getSound()->getSoundFlag(kEntityCoudert));
+ else
+ getSound()->excuseMe(kEntityCoudert);
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventCorpseFound && !getEvent(kEventCoudertAskTylerCompartment))
+ params->param3 = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+
+ if (getData()->direction != kDirectionUp)
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+ else
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(10, Coudert, updateFromTime, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->time, params->param1);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Coudert, updateFromTicks, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters
+// - EntityIndex
+IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ if (isNight()) {
+ if (Entities::isFemale((EntityIndex)params->param1)) {
+ getSound()->playSound(kEntityCoudert, Entities::isMarried((EntityIndex)params->param1) ? "JAC1112C" : "JAC1112F");
+ } else {
+ if (!params->param1 && getProgress().field_18 == 2) {
+ switch (rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityCoudert, "JAC1113");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityCoudert, "JAC1113A");
+ break;
+ }
+ } else {
+ getSound()->playSound(kEntityCoudert, "JAC1112D");
+ }
+ }
+ } else {
+ if (Entities::isFemale((EntityIndex)params->param1))
+ getSound()->playSound(kEntityCoudert, Entities::isMarried((EntityIndex)params->param1) ? "JAC1112B" : "JAC1112G");
+ else
+ getSound()->playSound(kEntityCoudert, "JAC1112E");
+ }
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ if (!params->param2 && !params->param3) {
+
+ if (!params->param4) {
+ params->param4 = getState()->timeTicks + 75;
+
+ if (!params->param4) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(4);
+ setup_function19(true);
+ break;
+ }
+ }
+
+ if (params->param4 < getState()->timeTicks) {
+ params->param4 = kTimeInvalid;
+
+ getData()->inventoryItem = kItemNone;
+ setCallback(4);
+ setup_function19(true);
+ break;
+ }
+ }
+
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 225);
+
+ getData()->inventoryItem = kItemNone;
+ setCallback(5);
+ setup_function19(true);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kAction11:
+ ++params->param3;
+
+ setCallback(8);
+ setup_excuseMe(savepoint.entity2);
+ break;
+
+ case kActionDefault:
+ if (params->param2)
+ params->param3 = 1;
+
+ setCallback(1);
+ setup_excuseMe((EntityIndex)params->param2);
+ break;
+
+ case kAction16:
+ --params->param3;
+
+ if (params->param2 && !params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_function19(true);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(6);
+ setup_function19(true);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(true);
+ break;
+
+ case 2:
+ if (getProgress().chapter == kChapter1 && !getProgress().eventCorpseFound && !getEvent(kEventCoudertAskTylerCompartment))
+ getData()->inventoryItem = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, params->param1 ? "667I" : "667H");
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ // BUG: the original game continues executing code here
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ CALLBACK_ACTION();
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+ break;
+ }
+ break;
+
+ case kAction201439712:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ } else {
+ setCallback(1);
+ setup_updateFromTime(15);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction202558662);
+
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction202558662);
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction125499160:
+ switch (params->param1) {
+ default:
+ break;
+
+ case kEntityVerges:
+ ENTITY_PARAM(0, 3) = 0;
+ break;
+
+ case kEntityMmeBoutarel:
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+
+ case kEntityMertens:
+ ENTITY_PARAM(0, 5) = 0;
+ break;
+ }
+
+ setCallback(5);
+ setup_function19(false);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(15, Coudert, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 8) = 0;
+ ENTITY_PARAM(1, 1) = 0;
+
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ if (params->param1)
+ getSound()->playSound(kEntityCoudert, "Tat3163");
+ else
+ getSound()->playSound(kEntityCoudert, (getProgress().chapter != kChapter3 || getState()->time > kTime1449000) ? "Tat3162A" : "Tat3161A");
+
+ setCallback(3);
+ setup_enterExitCompartment("627Xb", kObjectCompartmentB);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityTatiana, kAction69239528);
+ getData()->entityPosition = kPosition_7250;
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Coudert, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(ENTITY_PARAM(0, 2) ? 1 : 2);
+ setup_bloodJacket(ENTITY_PARAM(0, 2) ? "627C" : "627F");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param1) {
+ setCallback(1);
+ setup_bloodJacket("627H");
+ break;
+ }
+
+ if (params->param2) {
+ setCallback(2);
+ setup_bloodJacket("627C");
+ break;
+ }
+
+ setCallback(3);
+ setup_bloodJacket("627F");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Coudert, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 8)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7) || ENTITY_PARAM(1, 8)
+ || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) {
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 68)) {
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, "JAC1111");
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+ }
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityCoudert);
+ ENTITY_PARAM(2, 1) = 1;
+
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+
+ case 2:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ ENTITY_PARAM(0, 1) = 0;
+ getSavePoints()->push(kEntityCoudert, kEntityCoudert, kActionDrawScene);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 8)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7) || ENTITY_PARAM(1, 8)
+ || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) {
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) {
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ ENTITY_PARAM(2, 1) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param1)
+ getEntities()->drawSequenceRight(kEntityCoudert, "697H");
+ else
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 3), getState()->time, 300)
+ getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityCoudert));
+ UPDATE_PARAM_PROC_END
+
+ UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->time, 900);
+
+ getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1);
+
+ if (params->param4 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, (EntityIndex)params->param3, (ObjectLocation)params->param4, (CursorStyle)params->param5, (CursorStyle)params->param6);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param7, (ObjectLocation)params->param8, (CursorStyle)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2));
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param3 = getObjects()->get((ObjectIndex)params->param1).entity;
+ params->param4 = getObjects()->get((ObjectIndex)params->param1).location;
+ params->param5 = getObjects()->get((ObjectIndex)params->param1).cursor;
+ params->param6 = getObjects()->get((ObjectIndex)params->param1).cursor2;
+
+ if (params->param2) {
+ params->param7 = getObjects()->get((ObjectIndex)params->param2).entity;
+ params->param8 = getObjects()->get((ObjectIndex)params->param2).location;
+ CURRENT_PARAM(1, 1) = getObjects()->get((ObjectIndex)params->param2).cursor;
+ CURRENT_PARAM(1, 2) = getObjects()->get((ObjectIndex)params->param2).cursor2;
+
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ if (params->param4 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (params->param1 == kObjectCompartmentA || params->param1 == kObjectCompartmentC
+ || params->param1 == kObjectCompartmentG || params->param1 == kObjectCompartmentH) {
+ setCallback(3);
+ setup_playSound("Jac1001B");
+ } else {
+ setCallback(4);
+ setup_playSound("Jac1001A");
+ }
+ break;
+
+ case 3:
+ case 4:
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Coudert, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment("627Zh", kObjectCompartmentH);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vh", kObjectCompartmentH);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wh");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentH, kObjectNone);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("697Ah", kObjectCompartmentH);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentH, kObjectNone);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(9);
+ setup_enterExitCompartment("667Uh", kObjectCompartmentH);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment("667Th", kObjectCompartmentH);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Coudert, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment("627Rg", kObjectCompartmentG);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Mg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ng");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentG, kObjectNone);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Sg", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentG, kObjectNone);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(9);
+ setup_enterExitCompartment("627Ug", kObjectCompartmentG);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment("627Tg", kObjectCompartmentG);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1030");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Coudert, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vf", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kAction158007856);
+
+ setCallback(3);
+ setup_updateFromTime(150);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Coudert, visitCompartmentF)
+ visitCompartment(savepoint, kPosition_4070, "627Vf", kObjectCompartmentF, "627Wf", "627Zf", kPosition_4455, kObject53, "697Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Coudert, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Me", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_updateFromTime(45);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction254915200);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("627Re", kObjectCompartmentE, kPosition_4840, kPosition_4455);
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_function20(kObjectCompartmentE, kObject52);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Coudert, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment2("627Zd", kObjectCompartmentD, kPosition_5790, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vd", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wd");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentD, true);
+ break;
+
+ case 3:
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentD, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(getCallback() + 1);
+ setup_function20(kObjectCompartmentD, kObject51);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("697Ad", kObjectCompartmentD);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction122865568);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+
+ setCallback(9);
+ setup_enterExitCompartment("697Ad", kObjectCompartmentD);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment2("627Zd", kObjectCompartmentD, kPosition_5790, kPosition_6130);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Coudert, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Mc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Nc");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentC, kObject50);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Sc", kObjectCompartmentC);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentC, kObject50);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+
+ setCallback(9);
+ setup_enterExitCompartment("627Uc", kObjectCompartmentC);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Coudert, visitCompartmentB)
+ visitCompartment(savepoint, kPosition_7500, "627Vb", kObjectCompartmentB, "627Wb", "627Zb", kPosition_7850, kObject49, "697Ab");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Coudert, visitCompartmentA)
+ visitCompartment(savepoint, kPosition_8200, "627Ma", kObjectCompartmentA, "627Na", "627Ra", kPosition_7850, kObject48, "627Sa");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex)
+ // Expose parameters as IIIIIS and ignore the default exposed parameters
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+ EntityData::EntityParametersSIIS *parameters1 = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (parameters->param1) {
+ default:
+ CALLBACK_ACTION();
+ // Stop processing here
+ return;
+
+ case kObjectCompartmentA:
+ parameters->param2 = kPosition_8200;
+ parameters->param3 = kPosition_7850;
+ strcpy((char *)&parameters->seq, "627Ma");
+ strcpy((char *)&parameters1->seq1, "627Na");
+ break;
+
+ case kObjectCompartmentB:
+ parameters->param2 = kPosition_7500;
+ parameters->param3 = kPosition_7850;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vb");
+ strcpy((char *)&parameters1->seq1, "627Wb");
+ break;
+
+ case kObjectCompartmentC:
+ parameters->param2 = kPosition_6470;
+ parameters->param3 = kPosition_6130;
+ strcpy((char *)&parameters->seq, "627Mc");
+ strcpy((char *)&parameters1->seq1, "627Nc");
+ break;
+
+ case kObjectCompartmentD:
+ parameters->param2 = kPosition_5790;
+ parameters->param3 = kPosition_6130;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vd");
+ strcpy((char *)&parameters1->seq1, "627Wd");
+ break;
+
+ case kObjectCompartmentE:
+ parameters->param2 = kPosition_4840;
+ parameters->param3 = kPosition_4455;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Me");
+ strcpy((char *)&parameters1->seq1, "627Ne");
+ break;
+
+ case kObjectCompartmentF:
+ parameters->param2 = kPosition_4070;
+ parameters->param3 = kPosition_4455;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vf");
+ strcpy((char *)&parameters1->seq1, "627Wf");
+ break;
+ }
+
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, (EntityPosition)parameters->param2);
+ break;
+
+ case 2:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, (EntityPosition)parameters->param3)
+ || ((parameters->param1 == kObjectCompartmentE || parameters->param1 == kObjectCompartmentF) && getEntities()->isOutsideAnnaWindow())) {
+ getObjects()->update((ObjectIndex)parameters->param1, kEntityPlayer, getObjects()->get((ObjectIndex)parameters->param1).location, kCursorNormal, kCursorNormal);
+ parameters->param5 = true;
+ }
+
+ setCallback(3);
+ setup_enterExitCompartment((char *)&parameters->seq, (ObjectIndex)parameters->param1);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, (char *)&parameters1->seq1);
+ getEntities()->enterCompartment(kEntityCoudert, (ObjectIndex)parameters->param1, true);
+
+ setCallback(4);
+ setup_playSound(parameters->param4 ? "JAC3020" : "JAC3021");
+ break;
+
+ case 4:
+ if (parameters->param5)
+ getObjects()->update((ObjectIndex)parameters->param1, kEntityPlayer, getObjects()->get((ObjectIndex)parameters->param1).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityCoudert, (ObjectIndex)parameters->param1, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function18();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(31, Coudert, function31, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setCallback(3);
+ setup_function19(true);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_bloodJacket("627G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ } else {
+ setCallback(2);
+ setup_function19(true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Coudert, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityCoudert);
+ setCallback(3);
+ setup_updateFromTime(900);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Coudert, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 4) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)
+ || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(2, 6) = 1;
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 4) || ENTITY_PARAM(0, 5)) {
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ } else {
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ }
+ } else {
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(2, 1) = 1;
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(2);
+ setup_function14(kEntityVerges);
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(3);
+ setup_function14(kEntityMertens);
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ ENTITY_PARAM(2, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_updateFromTime(75);
+ break;
+
+ case 6:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) {
+ setCallback(7);
+ setup_function37();
+ break;
+ }
+ // Fallback to next case
+
+ case 7:
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(8);
+ setup_function39();
+ break;
+ }
+ // Fallback to next case
+
+ case 8:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(9);
+ setup_function55();
+ break;
+ }
+ // Fallback to next case
+
+ case 9:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(10);
+ setup_function34(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 10:
+ ENTITY_PARAM(2, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(34, Coudert, function34, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ if (!params->param1) {
+ getSound()->playSound(kEntityCoudert, "Ann3124");
+
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+
+ setCallback(7);
+ setup_function35((bool)params->param1);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Vf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("Jac1001");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityCoudert, "Ann3125");
+
+ setCallback(5);
+ setup_enterExitCompartment("629Bf", kObjectCompartmentF);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("629Ff", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+
+ setCallback(7);
+ setup_function35((bool)params->param1);
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage)) {
+ getAction()->playAnimation(kEventCoudertBaggageCar);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ UPDATE_PARAM(params->param2, getState()->time, 2700);
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage);
+
+ getData()->clothes = kClothesDefault;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionDefault:
+ if (params->param1)
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction156049968);
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kAction122358304);
+
+ getData()->clothes = kClothes1;
+ getData()->entityPosition = kPosition_4370;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, "Ann3124");
+
+ if (params->param1)
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction123733488);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityCoudert);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function18();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Coudert, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTimeChapter1, params->param1, 1, setup_chapter1Handler)
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityCoudert, kAction292048641, 7);
+ getSavePoints()->addData(kEntityCoudert, kAction326348944, 8);
+ getSavePoints()->addData(kEntityCoudert, kAction171394341, 2);
+ getSavePoints()->addData(kEntityCoudert, kAction154005632, 4);
+ getSavePoints()->addData(kEntityCoudert, kAction169557824, 3);
+ getSavePoints()->addData(kEntityCoudert, kAction226031488, 5);
+ getSavePoints()->addData(kEntityCoudert, kAction339669520, 6);
+ getSavePoints()->addData(kEntityCoudert, kAction189750912, 10);
+ getSavePoints()->addData(kEntityCoudert, kAction185737168, 12);
+ getSavePoints()->addData(kEntityCoudert, kAction185671840, 13);
+ getSavePoints()->addData(kEntityCoudert, kAction205033696, 15);
+ getSavePoints()->addData(kEntityCoudert, kAction157026693, 14);
+ getSavePoints()->addData(kEntityCoudert, kAction189026624, 11);
+ getSavePoints()->addData(kEntityCoudert, kAction168254872, 17);
+ getSavePoints()->addData(kEntityCoudert, kAction201431954, 18);
+ getSavePoints()->addData(kEntityCoudert, kAction188570113, 19);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 1;
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter1Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Coudert, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getSound()->isBuffered(kEntityCoudert))
+ getSound()->processEntry(kEntityCoudert);
+
+ if (ENTITY_PARAM(0, 7)) {
+ getData()->entityPosition = kPosition_8200;
+
+ setCallback(4);
+ setup_enterExitCompartment2("698Ha", kObjectCompartmentA, kPosition_8200, kPosition_7850);
+ } else {
+ setCallback(1);
+ setup_function16();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction238358920);
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("698Ha", kObjectCompartmentA, kPosition_8200, kPosition_7850);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Coudert, function38)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ setup_chapter1Handler();
+ break;
+ }
+ break;
+
+ case kAction191477936:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Coudert, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("LIB070");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("627Vd", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wd");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentD, true);
+
+ setCallback(5);
+ setup_playSound("MME1151A");
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentD, true);
+
+ setCallback(6);
+ setup_enterExitCompartment("627Zd", kObjectCompartmentD);
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(7);
+ setup_playSound("MME1151");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("MME1151", kObjectCompartmentD);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction223068211);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function18();
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction167854368);
+ ENTITY_PARAM(2, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Coudert, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(0, 1) = 1;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 2) = 0;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param1 = 1;
+ params->param2 = 1;
+
+ ENTITY_PARAM(2, 3) = 0;
+ }
+
+ getData()->inventoryItem = (getProgress().eventCorpseFound || getEvent(kEventCoudertAskTylerCompartment)) ? kItemNone : kItemInvalid;
+
+ if (ENTITY_PARAM(0, 8)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(1, 1)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(5);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setup_function37();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_6:
+ if (ENTITY_PARAM(0, 5)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(7);
+ setup_function14(kEntityMertens);
+ break;
+ }
+
+label_callback_7:
+ if (ENTITY_PARAM(0, 4)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(8);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+
+label_callback_8:
+ if (ENTITY_PARAM(2, 2)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_function39();
+ break;
+ }
+
+label_callback_9:
+ if (ENTITY_PARAM(0, 1) && !getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "JAC1065" : "JAC1065A");
+
+ if (getState()->time > kTime1107000 && !ENTITY_PARAM(0, 1) && !getEvent(kEventVassiliSeizure)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(10);
+ setup_function41();
+ break;
+ }
+
+label_callback_10:
+ if (getState()->time > kTime1189800 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 2700);
+ ENTITY_PARAM(0, 2) = 1;
+ ENTITY_PARAM(0, 1) = 1;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!ENTITY_PARAM(0, 2))
+ break;
+
+ TIME_CHECK_OBJECT(kTime1107000, params->param4, kObject111, kObjectLocation2);
+ TIME_CHECK_OBJECT(kTime1161000, params->param5, kObject111, kObjectLocation3);
+ TIME_CHECK_OBJECT(kTime1206000, params->param6, kObject111, kObjectLocation4);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(13);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 1) && !getEntities()->isPlayerPosition(kCarRedSleeping, 23))
+ break;
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket);
+ } else {
+ setCallback(getEntities()->isPlayerPosition(kCarRedSleeping, 1) ? 2 : 3);
+ setup_function13(true, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ params->param1 = 1;
+ goto label_callback_10;
+
+ case 11:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+
+ setCallback(12);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 12:
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction168253822:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityCoudert, "JAC1120");
+
+ setCallback(14);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(16);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityCoudert, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(17);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Coudert, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_visitCompartmentA();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_visitCompartmentB();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function27();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction285528346);
+
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function26();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function33();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function25();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function33();
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_function23();
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function33();
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_function22();
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function33();
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_function21();
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 17:
+ setCallback(18);
+ setup_function18();
+ break;
+
+ case 18:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction208228224);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Coudert, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation5);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function43();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Coudert, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function15(true);
+ break;
+ }
+
+label_callback1:
+ if (!ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_function15(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function14(kEntityVerges);
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(4);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(5);
+ setup_function13(true, kEntityPlayer);
+
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(6);
+ setup_function13(false, kEntityPlayer);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 7:
+ setCallback(8);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(7);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(10);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Coudert, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+ ENTITY_PARAM(2, 5) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation6);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Coudert, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(4);
+ setup_function14(kEntityMertens);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(1, 3)) {
+ setCallback(5);
+ setup_function46();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(6);
+ setup_function47(true);
+ break;
+ }
+
+label_callback_6:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(7);
+ setup_function47(false);
+ break;
+ }
+
+label_callback_7:
+ if (ENTITY_PARAM(1, 8)) {
+ setCallback(8);
+ setup_function48();
+ break;
+ }
+
+label_callback_8:
+ if (ENTITY_PARAM(2, 4)) {
+ setCallback(9);
+ setup_function49();
+ break;
+ }
+
+label_callback_9:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(10);
+ setup_function34(true);
+ break;
+ }
+
+label_callback_10:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(11);
+ setup_function34(false);
+ break;
+ }
+
+label_callback_11:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(12);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+
+label_callback_12:
+ // BUG: Can never be called... FAIL!
+ if (ENTITY_PARAM(2, 5) && getState()->time > kTime2056500 && getState()->time < kTime1417500) {
+ setCallback(13);
+ setup_function50();
+ break;
+ }
+
+label_callback_13:
+ TIME_CHECK_CALLBACK(kTime2088900, params->param1, 14, setup_function32);
+
+label_callback_14:
+ TIME_CHECK_CALLBACK(kTime2119500, params->param2, 15, setup_function32);
+
+label_callback_15:
+ TIME_CHECK_CALLBACK(kTime2138400, params->param3, 16, setup_function32);
+
+label_callback_16:
+ TIME_CHECK_CALLBACK(kTime2147400, params->param4, 17, setup_function32);
+
+label_callback_17:
+ TIME_CHECK_CALLBACK(kTime2160000, params->param5, 18, setup_function32);
+
+label_callback_18:
+ TIME_CHECK_CALLBACK(kTime2205000, params->param6, 19, setup_function32);
+
+label_callback_19:
+ if (ENTITY_PARAM(0, 2)) {
+ TIME_CHECK_OBJECT(kTime2025000, params->param7, kObject111, kObjectLocation7);
+ TIME_CHECK_OBJECT(kTime2133000, params->param8, kObject111, kObjectLocation8);
+ TIME_CHECK_OBJECT(kTime2173500, CURRENT_PARAM(1, 1), kObject111, kObjectLocation9);
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(20);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(21);
+ setup_function13(true, kEntityPlayer);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(22);
+ setup_function13(false, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction168255788);
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+
+ case 15:
+ goto label_callback_15;
+
+ case 16:
+ goto label_callback_16;
+
+ case 17:
+ goto label_callback_17;
+
+ case 18:
+ goto label_callback_18;
+
+ case 19:
+ goto label_callback_19;
+
+ case 23:
+ setCallback(24);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(25);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(23);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(26);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Coudert, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Vf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction253868128);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+
+ setCallback(4);
+ setup_playSound("Ann1016A");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("Ann4150");
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityCoudert, "Ann3121");
+
+ setCallback(6);
+ setup_enterExitCompartment("629Bf", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "629Cf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ // Fallback to next case
+
+ case 7:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ setCallback(7);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(8);
+ setup_playSound("Ann3122");
+ }
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "Ann3123");
+
+ setCallback(9);
+ setup_updateFromTicks(75);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_enterExitCompartment("629Ff", kObjectCompartmentF);
+ break;
+
+ case 10:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ ENTITY_PARAM(1, 3) = 0;
+
+ setCallback(11);
+ setup_function35(true);
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(47, Coudert, function47, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("627Xf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF);
+ // Fallback to next case
+
+ case 4:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ setCallback(4);
+ setup_updateFromTime(225);
+ } else {
+ setCallback(5);
+ setup_playSound(params->param1 ? "Ann3149" : "Ann3147a");
+ }
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction157894320);
+
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+
+ setCallback(7);
+ setup_function18();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Coudert, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityCoudert, "Ann3148A");
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Ann3148B" : "Ann3148");
+ setCallback(3);
+ setup_enterExitCompartment("627Xf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction192063264);
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ ENTITY_PARAM(1, 8) = 0;
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Coudert, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("627Vb", kObjectCompartmentB);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityTatiana, kCarRedSleeping, kPosition_7500)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+
+ setCallback(4);
+ setup_playSound("Jac3006");
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(8);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(5);
+ setup_enterExitCompartment("627Zb", kObjectCompartmentB);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_playSound("Jac3006A");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("697Ab", kObjectCompartmentB);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateFromTime(150);
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction242526416);
+ ENTITY_PARAM(2, 4) = 0;
+ ENTITY_PARAM(2, 5) = 1;
+
+ setCallback(11);
+ setup_function18();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Coudert, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Me");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ if (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840)) {
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(8);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+
+ setCallback(4);
+ setup_playSound("Jac3005");
+ }
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Re", kObjectCompartmentE);
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_playSound("Jac3005A");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ ENTITY_PARAM(2, 5) = 0;
+
+ setCallback(9);
+ setup_function18();
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Coudert, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2133000 && !getProgress().field_40) {
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (savepoint.param.intValue == kObjectCompartmentB)
+ getData()->entityPosition = kPosition_7500;
+
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventCoudertGoingOutOfVassiliCompartment);
+ getEntities()->updateEntity(kEntityCoudert, kCarRedSleeping, kPosition_2000);
+ getScenes()->loadSceneFromObject(savepoint.param.intValue == kObjectCompartmentB ? kObjectCompartmentB : kObjectCompartmentA);
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage);
+
+ if (ENTITY_PARAM(0, 5)) {
+ ENTITY_PARAM(0, 5) = 0;
+
+ getSavePoints()->push(kEntityCoudert, kEntityMertens, kAction155853632);
+ getSavePoints()->push(kEntityCoudert, kEntityMertens, kActionEndSound);
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ ENTITY_PARAM(0, 3) = 0;
+
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction155853632);
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kActionEndSound);
+ }
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentB, true);
+ getSavePoints()->push(kEntityCoudert, kEntityTatiana, kAction154071333);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ case 4:
+ case 6:
+ setup_function45();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function18();
+ break;
+
+ case 5:
+ setCallback(5);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction168316032:
+ getObjects()->update(kObjectCompartmentA, kEntityCoudert, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorHand);
+ break;
+
+ case kAction235061888:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Coudert, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 3) = 0;
+ ENTITY_PARAM(2, 4) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation10);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ ENTITY_PARAM(1, 2) = 1;
+ setup_function53();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Coudert, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ params->param1 = 1;
+
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentB, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentC, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentD, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentE, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentF, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentG, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentH, kObjectLocation1);
+
+ ENTITY_PARAM(2, 3) = 0;
+
+ setCallback(1);
+ setup_function54();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 2)) {
+ if (!params->param2)
+ params->param2 = (uint)(getState()->time + 4500);
+
+ if (params->param3 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param2, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param3, 0)
+ setCallback(2);
+ setup_function55();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(3);
+ setup_function34(false);
+ break;
+ }
+
+label_callback_3:
+ if (!params->param1) {
+ TIME_CHECK_CALLBACK(kTime2394000, params->param4, 4, setup_function56);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK(kTime2434500, params->param5, 5, setup_function32);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2448000, params->param6, 6, setup_function32);
+ }
+
+label_callback_6:
+ if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM(params->param7, getState()->time, 2700);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 1) = 1;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param7 = 0;
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(7);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(8);
+ setup_function13(true, kEntityPlayer);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(9);
+ setup_function13(false, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 10:
+ setCallback(11);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(12);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(10);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(13);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Coudert, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->hasValidFrame(kEntityCoudert)) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ } else {
+ getData()->car = kCarLocomotive;
+ getData()->entityPosition = kPosition_540;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityCoudert);
+ getData()->car = kCarLocomotive;
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction191001984:
+ getData()->car = kCarRedSleeping;
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Coudert, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("LIB070");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ setCallback(4);
+ setup_playSound("Ann4150A");
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction219971920);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction101824388);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityCoudert);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction136059947);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(7);
+ setup_function18();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, Coudert, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function21();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function22();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_visitCompartmentF();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function25();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function33();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function26();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function33();
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_function27();
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function33();
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_visitCompartmentB();
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_function18();
+ break;
+
+ case 16:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, Coudert, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, Coudert, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function59();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, Coudert, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getSound()->playSound(kEntityCoudert, "Jac5010"); // Situation is under control, please remain in your compartment
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ setup_function60();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, Coudert, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function61();
+ break;
+
+ case kAction155991520:
+ setCallback(1);
+ setup_updateFromTime(225);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, Coudert, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Me", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_updateFromTime(75);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(4);
+ setup_enterExitCompartment("627Re", kObjectCompartmentE);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_playSound("Reb5010");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction155604840);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("627Zh", kObjectCompartmentH);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction169750080);
+
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, Coudert, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ ++params->param3;
+
+ if (params->param3 == 1 || params->param2) {
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ setCallback(params->param3 == 1 ? 4 : 5);
+ setup_playSound(params->param3 == 1 ? "Jac5002" : "Jac5002A");
+ }
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 5:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(63, Coudert)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Coudert::visitCompartment(const SavePoint &savepoint, EntityPosition position, const char *seq1, ObjectIndex compartment, const char *seq2, const char *seq3, EntityPosition sittingPosition, ObjectIndex object, const char *seq4) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, position);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment(seq1, compartment);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, seq2);
+ getEntities()->enterCompartment(kEntityCoudert, compartment, true);
+
+ setCallback(3);
+ setup_updateFromTime(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2(seq3, compartment, position, sittingPosition);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, compartment, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(5);
+ setup_function20(compartment, object);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment(seq4, compartment);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/coudert.h b/engines/lastexpress/entities/coudert.h
new file mode 100644
index 0000000000..13dad6f122
--- /dev/null
+++ b/engines/lastexpress/entities/coudert.h
@@ -0,0 +1,229 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_COUDERT_H
+#define LASTEXPRESS_COUDERT_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Coudert : public Entity {
+public:
+ Coudert(LastExpressEngine *engine);
+ ~Coudert() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handle meeting Coudert with the blooded jacket
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(bloodJacket, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param entityPosition1 The entity position 1
+ * @param entityPosition2 The entity position 2
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment2, const char *sequence, ObjectIndex compartment, EntityPosition entityPosition1, EntityPosition entityPosition2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound16)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ DECLARE_FUNCTION_1(excuseMe, EntityIndex entity)
+ DECLARE_FUNCTION_2(function13, bool, EntityIndex entity)
+ DECLARE_FUNCTION_1(function14, EntityIndex entity)
+ DECLARE_FUNCTION_1(function15, bool)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION_1(function17, bool)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION_1(function19, bool)
+
+ /**
+ * ???
+ *
+ * @param object1 The first object index
+ * @param object2 The second object index
+ */
+ DECLARE_FUNCTION_2(function20, ObjectIndex object1, ObjectIndex object2)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(visitCompartmentF)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(visitCompartmentB)
+ DECLARE_FUNCTION(visitCompartmentA)
+
+ /**
+ * ???
+ *
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_1(function30, ObjectIndex compartment)
+
+ DECLARE_FUNCTION_1(function31, uint32)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION_1(function34, bool)
+ DECLARE_FUNCTION_1(function35, bool)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function43)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION_1(function47, bool)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void visitCompartment(const SavePoint &savepoint, EntityPosition position, const char *seq1, ObjectIndex compartment, const char *seq2, const char *seq3, EntityPosition sittingPosition, ObjectIndex object, const char *seq4);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_COUDERT_H
diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp
new file mode 100644
index 0000000000..38b3dec09f
--- /dev/null
+++ b/engines/lastexpress/entities/entity.cpp
@@ -0,0 +1,499 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/entities/entity_intern.h"
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// EntityData
+//////////////////////////////////////////////////////////////////////////
+
+EntityData::EntityCallData::~EntityCallData() {
+ SAFE_DELETE(frame);
+ SAFE_DELETE(frame1);
+
+ SAFE_DELETE(sequence);
+ SAFE_DELETE(sequence2);
+ SAFE_DELETE(sequence3);
+}
+
+void EntityData::EntityCallData::saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(callbacks); i++)
+ s.syncAsByte(callbacks[i]);
+
+ s.syncAsByte(currentCall);
+ s.syncAsUint16LE(entityPosition);
+ s.syncAsUint16LE(location);
+ s.syncAsUint16LE(car);
+ s.syncAsByte(field_497);
+ s.syncAsByte(entity);
+ s.syncAsByte(inventoryItem);
+ s.syncAsByte(direction);
+ s.syncAsUint16LE(field_49B);
+ s.syncAsUint16LE(currentFrame);
+ s.syncAsUint16LE(currentFrame2);
+ s.syncAsUint16LE(field_4A1);
+ s.syncAsUint16LE(field_4A3);
+ s.syncAsByte(clothes);
+ s.syncAsByte(position);
+ s.syncAsByte(car2);
+ s.syncAsByte(doProcessEntity);
+ s.syncAsByte(field_4A9);
+ s.syncAsByte(field_4AA);
+ s.syncAsByte(directionSwitch);
+
+ // Sync strings
+#define SYNC_STRING(varName, count) { \
+ char seqName[13]; \
+ memset(&seqName, 0, count); \
+ if (s.isSaving()) strcpy((char *)&seqName, varName.c_str()); \
+ s.syncBytes((byte *)&seqName, count); \
+ if (s.isLoading()) varName = seqName; \
+}
+
+ SYNC_STRING(sequenceName, 13);
+ SYNC_STRING(sequenceName2, 13);
+ SYNC_STRING(sequenceNamePrefix, 7);
+ SYNC_STRING(sequenceNameCopy, 13);
+
+#undef SYNC_STRING
+
+ // Skip pointers to frame & sequences
+ s.skip(5 * 4);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// EntityData
+//////////////////////////////////////////////////////////////////////////
+EntityData::EntityParameters *EntityData::getParameters(uint callback, byte index) const {
+ if (callback >= 9)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 9)", callback);
+
+ if (index >= 4)
+ error("EntityData::getParameters: invalid index value (was: %d, max: 4)", index);
+
+ return _parameters[callback].parameters[index];
+}
+
+int EntityData::getCallback(uint callback) const {
+ if (callback >= 16)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 16)", callback);
+
+ return _data.callbacks[callback];
+}
+
+void EntityData::setCallback(uint callback, byte index) {
+ if (callback >= 16)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 16)", callback);
+
+ _data.callbacks[callback] = index;
+}
+
+void EntityData::updateParameters(uint32 index) const {
+ if (index < 8)
+ getParameters(8, 0)->update(index);
+ else if (index < 16)
+ getParameters(8, 1)->update(index - 8);
+ else if (index < 24)
+ getParameters(8, 2)->update(index - 16);
+ else if (index < 32)
+ getParameters(8, 3)->update(index - 24);
+ else
+ error("EntityData::updateParameters: invalid param index to update (was:%d, max:32)!", index);
+}
+
+void EntityData::saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(_parameters); i++)
+ _parameters[i].saveLoadWithSerializer(s);
+
+ _data.saveLoadWithSerializer(s);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entity
+//////////////////////////////////////////////////////////////////////////
+Entity::Entity(LastExpressEngine *engine, EntityIndex index) : _engine(engine), _entityIndex(index) {
+ _data = new EntityData();
+
+ // Add first empty entry to callbacks array
+ _callbacks.push_back(NULL);
+}
+
+Entity::~Entity() {
+ for (uint i = 0; i < _callbacks.size(); i++)
+ SAFE_DELETE(_callbacks[i]);
+
+ _callbacks.clear();
+
+ SAFE_DELETE(_data);
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+void Entity::setup(ChapterIndex index) {
+ switch(index) {
+ case kChapterAll:
+ getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]);
+ break;
+
+ case kChapter1:
+ setup_chapter1();
+ break;
+
+ case kChapter2:
+ setup_chapter2();
+ break;
+
+ case kChapter3:
+ setup_chapter3();
+ break;
+
+ case kChapter4:
+ setup_chapter4();
+ break;
+
+ case kChapter5:
+ setup_chapter5();
+ break;
+
+ default:
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Shared functions
+//////////////////////////////////////////////////////////////////////////
+
+void Entity::reset(const SavePoint &savepoint, bool resetClothes, bool resetItem) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ if (resetClothes) {
+ // Select next available clothes
+ getData()->clothes = (ClothesIndex)(getData()->clothes + 1);
+ if (getData()->clothes > kClothes3)
+ getData()->clothes = kClothesDefault;
+ }
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, kCarGreenSleeping, (EntityPosition)params->param1))
+ params->param1 = (params->param1 == 10000) ? 0 : 10000;
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ if (resetItem)
+ getData()->inventoryItem = kItemInvalid;
+
+ params->param1 = 10000;
+ break;
+ }
+}
+
+void Entity::savegame(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSaveLoad()->saveGame((SavegameType)params->param1, _entityIndex, (EventIndex)params->param2);
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundManager::FlagType flag) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (resetItem)
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(_entityIndex, (char *)&params->seq1, flag);
+ break;
+ }
+}
+
+void Entity::draw(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !params->param4) {
+ getSound()->excuseMe(_entityIndex);
+ params->param4 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ break;
+ }
+}
+
+void Entity::draw2(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ getEntities()->drawSequenceRight((EntityIndex)params->param7, (char *)&params->seq2);
+ break;
+ }
+}
+
+void Entity::updateFromTicks(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::updateFromTime(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callbackActionOnDirection(const SavePoint &savepoint) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callbackActionRestaurantOrSalon(const SavePoint &savepoint) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon())
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::updateEntity(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe)
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ if (handleExcuseMe)
+ getSound()->excuseMe(_entityIndex);
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callSavepoint(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ if (!CURRENT_PARAM(1, 1))
+ getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)&params->seq2);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !CURRENT_PARAM(1, 2)) {
+ getSound()->excuseMe(_entityIndex);
+ CURRENT_PARAM(1, 2) = 1;
+ }
+ break;
+
+ case kAction10:
+ if (!CURRENT_PARAM(1, 1)) {
+ getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)&params->seq2);
+ CURRENT_PARAM(1, 1) = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ break;
+ }
+}
+
+void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition position1, EntityPosition position2, CarIndex car, ObjectIndex compartment, bool alternate, bool updateLocation) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4);
+ if (position1)
+ getData()->entityPosition = position1;
+
+ if (updateLocation)
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ getEntities()->enterCompartment(_entityIndex, (ObjectIndex)params->param4);
+
+ if (position1) {
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, car, position1) || getEntities()->isInsideCompartment(kEntityPlayer, car, position2)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(compartment, alternate);
+ }
+ }
+ break;
+ }
+}
+
+void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(_entityIndex, (CarIndex)params->param4, (Position)params->param5);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !params->param6) {
+ getSound()->excuseMe(_entityIndex);
+ params->param6 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq);
+ getEntities()->updatePositionEnter(_entityIndex, (CarIndex)params->param4, (Position)params->param5);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h
new file mode 100644
index 0000000000..ccef312cd6
--- /dev/null
+++ b/engines/lastexpress/entities/entity.h
@@ -0,0 +1,801 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY_H
+#define LASTEXPRESS_ENTITY_H
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/helpers.h"
+
+#include "common/array.h"
+#include "common/func.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+class SequenceFrame;
+struct SavePoint;
+
+class EntityData : Common::Serializable {
+public:
+
+ struct EntityParameters : Common::Serializable{
+ virtual ~EntityParameters() {}
+ virtual Common::String toString() = 0;
+
+ virtual void update(uint32 index) = 0;
+
+ virtual void saveLoadWithSerializer(Common::Serializer &s) = 0;
+ };
+
+ struct EntityParametersIIII : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ uint param4;
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersIIII() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ param4 = 0;
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ bool hasNonNullParameter() {
+ return param1 || param2 || param3 || param4 || param5 || param6 || param7 || param8;
+ }
+
+ Common::String toString() {
+ return Common::String::format("IIII: %d %d %d %d %d %d %d %d\n", param1, param2, param3, param4, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIIII::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 2: param3 = 1; break;
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncAsUint32LE(param3);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSIII : EntityParameters {
+ char seq[12];
+ uint param4;
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersSIII() {
+ memset(&seq, 0, 12);
+ param4 = 0;
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("SIII: %s %d %d %d %d %d\n", seq, param4, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSIII::update: invalid index (was: %d)", index);
+
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSIIS : EntityParameters {
+ char seq1[12];
+ uint param4;
+ uint param5;
+ char seq2[12];
+
+ EntityParametersSIIS() {
+ memset(&seq1, 0, 12);
+ param4 = 0;
+ param5 = 0;
+ memset(&seq2, 0, 12);
+ }
+
+ Common::String toString() {
+ return Common::String::format("SIIS: %s %d %d %s\n", seq1, param4, param5, seq2);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSIIS::update: invalid index (was: %d)", index);
+
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncBytes((byte *)&seq2, 12);
+ }
+ };
+
+ struct EntityParametersISSI : EntityParameters {
+ uint param1;
+ char seq1[12];
+ char seq2[12];
+ uint param8;
+
+ EntityParametersISSI() {
+ param1 = 0;
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("ISSI: %d %s %s %d\n", param1, seq1, seq2, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersISSI::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersISII : EntityParameters {
+ uint param1;
+ char seq[12];
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersISII() {
+ param1 = 0;
+ memset(&seq, 0, 12);
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("ISII: %d %s %d %d %d %d\n", param1, seq, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersISII::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param5);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSSII : EntityParameters {
+ char seq1[12];
+ char seq2[12];
+ uint param7;
+ uint param8;
+
+ EntityParametersSSII() {
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("SSII: %s %s %d %d\n", seq1, seq2, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSSII::update: invalid index (was: %d)", index);
+
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSSS : EntityParameters {
+ char seq1[12];
+ char seq2[12];
+ char seq3[8];
+
+ EntityParametersSSS() {
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ memset(&seq3, 0, 8);
+ }
+
+ Common::String toString() {
+ return Common::String::format("SSS: %s %s %s\n", seq1, seq2, seq3);
+ }
+
+ void update(uint32) {
+ error("EntityParametersSSS::update: cannot update this type of parameters");
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ s.syncBytes((byte *)&seq3, 8);
+ }
+ };
+
+ struct EntityParametersIISS : EntityParameters {
+ uint param1;
+ uint param2;
+ char seq1[12];
+ char seq2[12];
+
+ EntityParametersIISS() {
+ param1 = 0;
+ param2 = 0;
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ }
+
+ Common::String toString() {
+ return Common::String::format("IISS: %d %d %s %s\n", param1, param2, seq1, seq2);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIISS::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ }
+ };
+
+ struct EntityParametersIISI : EntityParameters {
+ uint param1;
+ uint param2;
+ char seq[12];
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersIISI() {
+ param1 = 0;
+ param2 = 0;
+ memset(&seq, 0, 12);
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("IISI: %d %d %s %d %d %d\n", param1, param2, seq, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIISI::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersIIIS : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ char seq[12];
+ uint param7;
+ uint param8;
+
+ EntityParametersIIIS() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ memset(&seq, 0, 12);
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("IIIS: %d %d %d %s %d %d\n", param1, param2, param3, seq, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIIIS::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 2: param3 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncAsUint32LE(param3);
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersI5S : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ uint param4;
+ uint param5;
+ char seq[12];
+
+ EntityParametersI5S() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ param4 = 0;
+ param5 = 0;
+ memset(&seq, 0, 12);
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncAsUint32LE(param3);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncBytes((byte *)&seq, 12);
+ }
+ };
+
+ struct EntityCallParameters : Common::Serializable {
+ EntityParameters *parameters[4];
+
+ EntityCallParameters() {
+ // We default to int parameters
+ for (int i = 0; i < 4; i++)
+ parameters[i] = new EntityParametersIIII();
+ }
+
+ ~EntityCallParameters() {
+ clear();
+ }
+
+ void clear() {
+ for (int i = 0; i < 4; i++)
+ SAFE_DELETE(parameters[i]);
+ }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(parameters); i++)
+ parameters[i]->saveLoadWithSerializer(s);
+ }
+ };
+
+ struct EntityCallData : Common::Serializable {
+ byte callbacks[16];
+ byte currentCall;
+ EntityPosition entityPosition; // word
+ Location location; // word
+ CarIndex car; // word
+ byte field_497;
+ EntityIndex entity; // byte
+ InventoryItem inventoryItem; // byte
+ EntityDirection direction; // byte
+ int16 field_49B;
+ int16 currentFrame;
+ int16 currentFrame2;
+ int16 field_4A1;
+ int16 field_4A3;
+ ClothesIndex clothes; // byte
+ Position position;
+ CarIndex car2; // byte
+ bool doProcessEntity; // byte
+ bool field_4A9; // byte
+ bool field_4AA; // byte
+ EntityDirection directionSwitch;
+ Common::String sequenceName; // char[13]
+ Common::String sequenceName2; // char[13]
+ Common::String sequenceNamePrefix; // char[7]
+ Common::String sequenceNameCopy; // char[13]
+ SequenceFrame *frame;
+ SequenceFrame *frame1;
+ Sequence *sequence;
+ Sequence *sequence2;
+ Sequence *sequence3;
+
+ /**
+ * Default constructor.
+ */
+ EntityCallData() {
+ memset(&callbacks, 0, 16 * sizeof(byte));
+ currentCall = 0;
+ entityPosition = kPositionNone;
+ location = kLocationOutsideCompartment;
+ car = kCarNone;
+ field_497 = 0;
+ entity = kEntityPlayer;
+ inventoryItem = kItemNone;
+ direction = kDirectionNone;
+ field_49B = 0;
+ currentFrame = 0;
+ currentFrame2 = 0;
+ field_4A1 = 0;
+ field_4A3 = 30;
+ clothes = kClothesDefault;
+ position = 0;
+ car2 = kCarNone;
+ doProcessEntity = false;
+ field_4A9 = false;
+ field_4AA = false;
+ directionSwitch = kDirectionNone;
+ frame = NULL;
+ frame1 = NULL;
+ sequence = NULL;
+ sequence2 = NULL;
+ sequence3 = NULL;
+ }
+
+ ~EntityCallData();
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String str = "";
+
+ str += Common::String::format("Entity position: %d - Location: %d - Car: %d\n", entityPosition, location, car);
+ str += Common::String::format("Entity: %d - Item: %d - Direction: %d\n", entity, inventoryItem, direction);
+ str += Common::String::format("Clothes: %d - Position: %d - Direction switch: %d\n", clothes, position, directionSwitch);
+ str += "\n";
+ str += Common::String::format("field_497: %02d - field_49B: %i - field_4A1: %i\n", field_497, field_49B, field_4A1);
+ str += Common::String::format("field_4A9: %02d - field_4AA: %i - Car 2: %d\n", field_4A9, field_4AA, car2);
+ str += "\n";
+ str += "Sequence: " + sequenceName + " - Sequence 2: " + sequenceName2 + "\n";
+ str += "Sequence prefix: " + sequenceNamePrefix + " - Sequence copy: " + sequenceNameCopy + "\n";
+ str += Common::String::format("Current frame: %i - Current frame 2: %i - Process entity: %d\n", currentFrame, currentFrame2, doProcessEntity);
+ str += "\n";
+ str += Common::String::format("Current call: %d\n", currentCall);
+ str += Common::String::format("Functions: %d %d %d %d %d %d %d %d\n", callbacks[0], callbacks[1], callbacks[2], callbacks[3], callbacks[4], callbacks[5], callbacks[6], callbacks[7]);
+ str += Common::String::format("Callbacks: %d %d %d %d %d %d %d %d\n", callbacks[8], callbacks[9], callbacks[10], callbacks[11], callbacks[12], callbacks[13], callbacks[14], callbacks[15]);
+
+ return str;
+ }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+ };
+
+ EntityData() {}
+
+ template<class T>
+ void resetCurrentParameters() {
+ EntityCallParameters *params = &_parameters[_data.currentCall];
+ params->clear();
+
+ for (int i = 0; i < 4; i++)
+ params->parameters[i] = new T();
+ }
+
+ EntityCallData *getCallData() { return &_data; }
+
+ EntityParameters *getParameters(uint callback, byte index) const;
+ EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); }
+
+ int getCallback(uint callback) const;
+ int getCurrentCallback() { return getCallback(_data.currentCall); }
+ void setCallback(uint callback, byte index);
+ void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); }
+
+ void updateParameters(uint32 index) const;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+private:
+
+ EntityCallData _data;
+ EntityCallParameters _parameters[9];
+};
+
+class Entity : Common::Serializable {
+public:
+
+ typedef Common::Functor1<const SavePoint&, void> Callback;
+
+ Entity(LastExpressEngine *engine, EntityIndex index);
+ virtual ~Entity();
+
+ // Accessors
+ EntityData *getParamData() { return _data; }
+ EntityData::EntityCallData *getData() { return _data->getCallData(); }
+
+ // Callbacks
+ byte getCallback() { return _data->getCallback(_data->getCallData()->currentCall + 8); }
+ void setCallback(byte index) { _data->setCallback(_data->getCallData()->currentCall + 8, index); getData()->currentCall++; }
+
+ // Setup
+ void setup(ChapterIndex index);
+
+ virtual void setup_chapter1() = 0;
+ virtual void setup_chapter2() = 0;
+ virtual void setup_chapter3() = 0;
+ virtual void setup_chapter4() = 0;
+ virtual void setup_chapter5() = 0;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser) { _data->saveLoadWithSerializer(ser); }
+
+ void nullfunction(const SavePoint &savepoint) {}
+
+protected:
+ LastExpressEngine *_engine;
+
+ EntityIndex _entityIndex;
+ EntityData *_data;
+ Common::Array<Callback *> _callbacks;
+
+ /**
+ * Saves the game
+ *
+ * @param savepoint The savepoint
+ * - SavegameType
+ * - EventIndex
+ */
+ void savegame(const SavePoint &savepoint);
+
+ /**
+ * Play sound
+ *
+ * @param savepoint The savepoint
+ * - Sound filename
+ * @param resetItem true to reset item.
+ * @param flag sound flag
+ */
+ void playSound(const SavePoint &savepoint, bool resetItem = false, SoundManager::FlagType flag = SoundManager::kFlagInvalid);
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - Sequence
+ * - ExcuseMe flag
+ * @param handleExcuseMe true to handle excuseMeCath action
+ */
+ void draw(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint.
+ * - Sequence 1
+ * - Sequence 2
+ * - EntityIndex
+ */
+ void draw2(const SavePoint &savepoint);
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - Number of ticks to add
+ */
+ void updateFromTicks(const SavePoint &savepoint);
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint.
+ * - Time to add
+ */
+ void updateFromTime(const SavePoint &savepoint);
+
+ /**
+ * Resets an entity
+ *
+ * @param savepoint The savepoint.
+ * @param resetClothes true to reset clothes.
+ * @param resetItem true to reset inventoryItem to kItemInvalid
+ */
+ void reset(const SavePoint &savepoint, bool resetClothes = false, bool resetItem = false);
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ *
+ * @param savepoint The savepoint.
+ */
+ void callbackActionOnDirection(const SavePoint &savepoint);
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ *
+ * @param savepoint The savepoint.
+ */
+ void callbackActionRestaurantOrSalon(const SavePoint &savepoint);
+
+ /**
+ * Updates the entity
+ *
+ * @param savepoint The savepoint.
+ * - CarIndex
+ * - EntityPosition
+ * @param handleExcuseMe true to handle the kActionExcuseMe/kActionExcuseMeCath actions.
+ */
+ void updateEntity(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Call a specific savepoint (or draw sequence in default case)
+ *
+ * @param savepoint The savepoint.
+ * - Sequence to draw in default case
+ * - EntityIndex
+ * - ActionIndex
+ * - Sequence for the savepoint
+ * @param handleExcuseMe true to handle excuse me.
+ */
+ void callSavepoint(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param savepoint The savepoint.
+ * @param position1 The first position.
+ * @param position2 The second position.
+ * @param car The car.
+ * @param compartment The compartment.
+ * @param alternate true to use the alternate version of SceneManager::loadSceneFromObject()
+ */
+ void enterExitCompartment(const SavePoint &savepoint, EntityPosition position1 = kPositionNone, EntityPosition position2 = kPositionNone, CarIndex car = kCarNone, ObjectIndex compartment = kObjectNone, bool alternate = false, bool updateLocation = false);
+
+ /**
+ * Updates the position
+ *
+ * @param savepoint The savepoint
+ * - Sequence name
+ * - CarIndex
+ * - Position
+ * @param handleExcuseMe true to handle excuseMe actions
+ */
+ void updatePosition(const SavePoint &savepoint, bool handleExcuseMe = false);
+};
+
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITY_H
diff --git a/engines/lastexpress/entities/entity39.cpp b/engines/lastexpress/entities/entity39.cpp
new file mode 100644
index 0000000000..6d139094c7
--- /dev/null
+++ b/engines/lastexpress/entities/entity39.cpp
@@ -0,0 +1,103 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/entity39.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Entity39::Entity39(LastExpressEngine *engine) : Entity(engine, kEntity39) {
+ ADD_CALLBACK_FUNCTION(Entity39, chapter1);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter2);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter3);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter4);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter5);
+ ADD_CALLBACK_FUNCTION(Entity39, process);
+
+ memset(&_sequence, 0, 12);
+ _counter = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Entity39, chapter1)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Entity39, chapter2)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Entity39, chapter3)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Entity39, chapter4)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Entity39, chapter5)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Entity39, process)
+// TODO: _sequence & counter do not seem to be touched anywhere else in the code :(
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->drawSequenceRight(kEntity39, (char *)&_sequence);
+ break;
+
+ case kActionNone:
+ getData()->car = getEntityData(kEntityPlayer)->car;
+
+ if (*_sequence && !_counter) {
+ _counter++;
+ getEntities()->drawSequenceRight(kEntity39, (char *)&_sequence);
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity39.h b/engines/lastexpress/entities/entity39.h
new file mode 100644
index 0000000000..344cf5b494
--- /dev/null
+++ b/engines/lastexpress/entities/entity39.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY39_H
+#define LASTEXPRESS_ENTITY39_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Entity39 : public Entity {
+public:
+ Entity39(LastExpressEngine *engine);
+ ~Entity39() {}
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Process function
+ */
+ DECLARE_FUNCTION(process)
+
+private:
+ char _sequence[12];
+ int _counter;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_##define##_H
diff --git a/engines/lastexpress/entities/entity_intern.h b/engines/lastexpress/entities/entity_intern.h
new file mode 100644
index 0000000000..6f80d29d8c
--- /dev/null
+++ b/engines/lastexpress/entities/entity_intern.h
@@ -0,0 +1,528 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY_INTERN_H
+#define LASTEXPRESS_ENTITY_INTERN_H
+
+namespace LastExpress {
+
+#define LOW_BYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff))
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+#define ENTITY_CALLBACK(class, name, pointer) \
+ Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name)
+
+#define ADD_CALLBACK_FUNCTION(class, name) \
+ _callbacks.push_back(new ENTITY_CALLBACK(class, name, this));
+
+#define ADD_NULL_FUNCTION() \
+ _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this));
+
+//////////////////////////////////////////////////////////////////////////
+// Declaration
+//////////////////////////////////////////////////////////////////////////
+
+#define DECLARE_FUNCTION(name) \
+ void setup_##name(); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_1(name, param1) \
+ void setup_##name(param1); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_2(name, param1, param2) \
+ void setup_##name(param1, param2); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_3(name, param1, param2, param3) \
+ void setup_##name(param1, param2, param3); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \
+ void setup_##name(param1, param2, param3, param4); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_NOSETUP(name) \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_NULL_FUNCTION() \
+ void setup_nullfunction();
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+
+#define IMPLEMENT_SETUP(class, callback_class, name, index) \
+void class::setup_##name() { \
+ BEGIN_SETUP(callback_class, name, index, EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::setup_" #name "()"); \
+ END_SETUP() \
+}
+
+#define BEGIN_SETUP(class, name, index, type) \
+ _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); \
+ _data->setCurrentCallback(index); \
+ _data->resetCurrentParameters<type>();
+
+#define END_SETUP() \
+ _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault);
+
+
+//////////////////////////////////////////////////////////////////////////
+// Implementation
+//////////////////////////////////////////////////////////////////////////
+
+// Expose parameters and check validity
+#define EXPOSE_PARAMS(type) \
+ type *params = (type*)_data->getCurrentParameters(); \
+ if (!params) \
+ error("Trying to call an entity function with invalid parameters!"); \
+
+
+// function signature without setup (we keep the index for consistency but never use it)
+#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \
+ void class::name(const SavePoint &savepoint) { \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")");
+
+// simple setup with no parameters
+#define IMPLEMENT_FUNCTION(index, class, name) \
+ IMPLEMENT_SETUP(class, class, name, index) \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action));
+
+// nullfunction call
+#define IMPLEMENT_NULL_FUNCTION(index, class) \
+ IMPLEMENT_SETUP(class, Entity, nullfunction, index)
+
+// setup with one uint parameter
+#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \
+ void class::setup_##name(paramType param1) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = (unsigned int)param1; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action));
+
+// setup with two uint parameters
+#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action));
+
+// setup with three uint parameters
+#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ params->param3 = param3; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter
+#define IMPLEMENT_FUNCTION_S(index, class, name) \
+ void class::setup_##name(const char *seq1) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)&params->seq1, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter and one uint
+#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \
+ void class::setup_##name(const char *seq1, paramType2 param4) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)&params->seq1, params->param4, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter and two uints
+#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \
+ void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)&params->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter and three uints
+#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \
+ void class::setup_##name(const char *seq, paramType2 param4, paramType3 param5, paramType4 param6) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIII) \
+ EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq, seq, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ params->param6 = param6; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)&params->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \
+ void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)&params->seq1, params->param4, params->param5, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SS(index, class, name) \
+ void class::setup_##name(const char *seq1, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \
+ EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \
+ void class::setup_##name(const char *seq1, const char *seq2, paramType3 param7) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \
+ EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ params->param7 = param7; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)&params->seq1, (char *)&params->seq2, params->param7, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \
+ void class::setup_##name(paramType param1, const char *seq) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersISII) \
+ EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII*)_data->getCurrentParameters(); \
+ params->param1 = (unsigned int)param1; \
+ strncpy((char *)&params->seq, seq, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersISII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)&params->seq, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \
+ void class::setup_##name(paramType param1, const char *seq1, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersISSI) \
+ EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersISSI) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISI) \
+ EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ strncpy((char *)&params->seq, seq, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIISI) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)&params->seq, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq1, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISS) \
+ EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIISS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+#define RESET_ENTITY_STATE(entity, class, function) \
+ getEntities()->resetState(entity); \
+ ((class*)getEntities()->get(entity))->function();
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters macros (for default IIII parameters)
+//////////////////////////////////////////////////////////////////////////
+#define CURRENT_PARAM(index, id) \
+ ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id
+
+#define ENTITY_PARAM(index, id) \
+ ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id
+
+//////////////////////////////////////////////////////////////////////////
+// Time check macros
+//////////////////////////////////////////////////////////////////////////
+#define TIME_CHECK(timeValue, parameter, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_SAVEPOINT(timeValue, parameter, entity1, entity2, action) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getSavePoints()->push(entity1, entity2, action); \
+ }
+
+#define TIME_CHECK_CALLBACK(timeValue, parameter, callback, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_1(timeValue, parameter, callback, function, param1) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_2(timeValue, parameter, callback, function, param1, param2) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1, param2); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_3(timeValue, parameter, callback, function, param1, param2, param3) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1, param2, param3); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_INVENTORY(timeValue, parameter, callback, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->inventoryItem = kItemNone; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_ACTION(timeValue, parameter) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ CALLBACK_ACTION(); \
+ break; \
+ }
+
+#define TIME_CHECK_PLAYSOUND_UPDATEPOSITION(timeValue, parameter, callback, sound, position) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->entityPosition = position; \
+ setCallback(callback); \
+ setup_playSound(sound); \
+ break; \
+ }
+
+#define TIME_CHECK_OBJECT(timeValue, parameter, object, location) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getObjects()->updateLocation2(object, location); \
+ }
+
+#define TIME_CHECK_CAR(timeValue, parameter, callback, function) {\
+ if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) \
+ parameter = (uint)getState()->time + 75; \
+ if (getState()->time > timeValue || parameter < getState()->time) { \
+ parameter = kTimeInvalid; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ } \
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callback action
+//////////////////////////////////////////////////////////////////////////
+#define CALLBACK_ACTION() { \
+ if (getData()->currentCall == 0) \
+ error("CALLBACK_ACTION: currentCall is already 0, cannot proceed!"); \
+ getData()->currentCall--; \
+ getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); \
+ getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); \
+ }
+
+//////////////////////////////////////////////////////////////////////////
+// Param update
+//////////////////////////////////////////////////////////////////////////
+#define UPDATE_PARAM(parameter, type, value) { \
+ if (!parameter) \
+ parameter = (uint)(type + value); \
+ if (parameter >= type) \
+ break; \
+ parameter = kTimeInvalid; \
+}
+
+// Todo: replace with UPDATE_PARAM_PROC as appropriate
+#define UPDATE_PARAM_GOTO(parameter, type, value, label) { \
+ if (!parameter) \
+ parameter = (uint)(type + value); \
+ if (parameter >= type) \
+ goto label; \
+ parameter = kTimeInvalid; \
+}
+
+// Updating parameter with code inside the check
+#define UPDATE_PARAM_PROC(parameter, type, value) \
+ if (!parameter) \
+ parameter = (uint)(type + value); \
+ if (parameter < type) { \
+ parameter = kTimeInvalid;
+
+#define UPDATE_PARAM_PROC_TIME(timeValue, test, parameter, value) \
+ if (getState()->time <= timeValue) { \
+ if (test || !parameter) \
+ parameter = (uint)(getState()->time + value); \
+ } \
+ if (parameter < getState()->time || getState()->time > timeValue) { \
+ parameter = kTimeInvalid;
+
+#define UPDATE_PARAM_PROC_END }
+
+// Updating parameter with an added check (and code inside the check)
+#define UPDATE_PARAM_CHECK(parameter, type, value) \
+ if (!parameter || parameter < type) { \
+ if (!parameter) \
+ parameter = (uint)(type + value);
+
+//////////////////////////////////////////////////////////////////////////
+// Compartments
+//////////////////////////////////////////////////////////////////////////
+// Go from one compartment to another (or the same one if no optional args are passed
+#define COMPARTMENT_TO(class, compartmentFrom, positionFrom, sequenceFrom, sequenceTo) \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionDefault: \
+ getData()->entityPosition = positionFrom; \
+ setCallback(1); \
+ setup_enterExitCompartment(sequenceFrom, compartmentFrom); \
+ break; \
+ case kActionCallback: \
+ switch (getCallback()) { \
+ default: \
+ break; \
+ case 1: \
+ setCallback(2); \
+ setup_enterExitCompartment(sequenceTo, compartmentFrom); \
+ break; \
+ case 2: \
+ getData()->entityPosition = positionFrom; \
+ getEntities()->clearSequences(_entityIndex); \
+ CALLBACK_ACTION(); \
+ } \
+ break; \
+ }
+
+#define COMPARTMENT_FROM_TO(class, compartmentFrom, positionFrom, sequenceFrom, compartmentTo, positionTo, sequenceTo) \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionDefault: \
+ getData()->entityPosition = positionFrom; \
+ getData()->location = kLocationOutsideCompartment; \
+ setCallback(1); \
+ setup_enterExitCompartment(sequenceFrom, compartmentFrom); \
+ break; \
+ case kActionCallback: \
+ switch (getCallback()) { \
+ default: \
+ break; \
+ case 1: \
+ setCallback(2); \
+ setup_updateEntity(kCarGreenSleeping, positionTo); \
+ break; \
+ case 2: \
+ setCallback(3); \
+ setup_enterExitCompartment(sequenceTo, compartmentTo); \
+ break; \
+ case 3: \
+ getData()->location = kLocationInsideCompartment; \
+ getEntities()->clearSequences(_entityIndex); \
+ CALLBACK_ACTION(); \
+ break; \
+ } \
+ break; \
+ }
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITY_INTERN_H
diff --git a/engines/lastexpress/entities/francois.cpp b/engines/lastexpress/entities/francois.cpp
new file mode 100644
index 0000000000..bec164e116
--- /dev/null
+++ b/engines/lastexpress/entities/francois.cpp
@@ -0,0 +1,1295 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/francois.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Francois::Francois(LastExpressEngine *engine) : Entity(engine, kEntityFrancois) {
+ ADD_CALLBACK_FUNCTION(Francois, reset);
+ ADD_CALLBACK_FUNCTION(Francois, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Francois, draw);
+ ADD_CALLBACK_FUNCTION(Francois, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Francois, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Francois, playSound);
+ ADD_CALLBACK_FUNCTION(Francois, savegame);
+ ADD_CALLBACK_FUNCTION(Francois, updateEntity);
+ ADD_CALLBACK_FUNCTION(Francois, function9);
+ ADD_CALLBACK_FUNCTION(Francois, function10);
+ ADD_CALLBACK_FUNCTION(Francois, function11);
+ ADD_CALLBACK_FUNCTION(Francois, function12);
+ ADD_CALLBACK_FUNCTION(Francois, function13);
+ ADD_CALLBACK_FUNCTION(Francois, function14);
+ ADD_CALLBACK_FUNCTION(Francois, function15);
+ ADD_CALLBACK_FUNCTION(Francois, function16);
+ ADD_CALLBACK_FUNCTION(Francois, chapter1);
+ ADD_CALLBACK_FUNCTION(Francois, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function19);
+ ADD_CALLBACK_FUNCTION(Francois, function20);
+ ADD_CALLBACK_FUNCTION(Francois, chapter2);
+ ADD_CALLBACK_FUNCTION(Francois, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function23);
+ ADD_CALLBACK_FUNCTION(Francois, chapter3);
+ ADD_CALLBACK_FUNCTION(Francois, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Francois, chapter4);
+ ADD_CALLBACK_FUNCTION(Francois, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Francois, chapter5);
+ ADD_CALLBACK_FUNCTION(Francois, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function30);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Francois, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, Francois, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Francois, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Francois, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, Francois, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_5790, kPosition_6130, kCarRedSleeping, kObjectCompartmentD, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Francois, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Francois, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000)
+ || !getInventory()->hasItem(kItemFirebird)
+ || getEvent(kEventFrancoisShowEgg)
+ || getEvent(kEventFrancoisShowEggD)
+ || getEvent(kEventFrancoisShowEggNight)
+ || getEvent(kEventFrancoisShowEggNightD)) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000)
+ && getInventory()->get(kItemBeetle)->location == kObjectLocation1
+ && !getEvent(kEventFrancoisShowBeetle)
+ && !getEvent(kEventFrancoisShowBeetleD))
+ getData()->inventoryItem = kItemMatchBox;
+ } else {
+ getData()->inventoryItem = kItemFirebird;
+ }
+
+ if (ENTITY_PARAM(0, 1)
+ && getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisTradeWhistle);
+ }
+ }
+ break;
+
+ case kAction1:
+ switch (savepoint.param.intValue) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisShowBeetle);
+ break;
+
+ case 18:
+ if (isNight())
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisShowEggNightD : kEventFrancoisShowEggNight);
+ else
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisShowEggD : kEventFrancoisShowEgg);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ getSound()->excuseMe(_entityIndex);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisTradeWhistleD : kEventFrancoisTradeWhistle);
+ getInventory()->addItem(kItemWhistle);
+ getInventory()->removeItem(kItemMatchBox);
+ getInventory()->get(kItemBeetle)->location = kObjectLocation2;
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ ENTITY_PARAM(0, 1) = 0;
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisShowBeetleD : kEventFrancoisShowBeetle);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Francois, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getObjects()->get(kObjectCompartmentD).location == kObjectLocation2) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction134289824);
+ setCallback(1);
+ setup_enterExitCompartment("605Cd", kObjectCompartmentD);
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("605Ed", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Francois, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getObjects()->get(kObjectCompartmentD).location == kObjectLocation2) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(1);
+ setup_enterExitCompartment("605Bd", kObjectCompartmentD);
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("605Dd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction102484312);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityFrancois)) {
+
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->timeTicks, params->param6)
+ switch (rnd(7)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityFrancois, "Fra1002A");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityFrancois, "Fra1002B");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityFrancois, "Fra1002C");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityFrancois, "Fra1002D");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityFrancois, "Fra1002E");
+ break;
+
+ case 5:
+ case 6:
+ getSound()->playSound(kEntityFrancois, "Fra1002F");
+ break;
+ }
+
+ params->param6 = 15 * rnd(7);
+ CURRENT_PARAM(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->hasValidFrame(kEntityFrancois) || !getEntities()->isWalkingOppositeToPlayer(kEntityFrancois))
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityFrancois, (CarIndex)params->param2, (EntityPosition)params->param3)) {
+ params->param5 = 0;
+
+ if (params->param3 == kPosition_540) {
+ params->param2 = (getProgress().chapter == kChapter1) ? kCarRedSleeping : kCarGreenSleeping;
+ params->param3 = kPosition_9460;
+ } else {
+ params->param2 = kCarGreenSleeping;
+ params->param3 = kPosition_540;
+ params->param7 = 0;
+ params->param8 = 0;
+
+ getSavePoints()->push(kEntityFrancois, kEntityCoudert, kAction225932896);
+ getSavePoints()->push(kEntityFrancois, kEntityMertens, kAction225932896);
+ }
+ }
+
+ if (getEntities()->checkDistanceFromPosition(kEntityFrancois, kPosition_2000, 500) && getData()->direction == kDirectionDown) {
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarRedSleeping) && params->param8) {
+ setCallback(2);
+ setup_draw("605A");
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarGreenSleeping) && params->param7) {
+ setCallback(3);
+ setup_draw("605A");
+ break;
+ }
+ }
+
+label_callback:
+ if (getProgress().chapter == kChapter1) {
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarRedSleeping)
+ && (getEntities()->hasValidFrame(kEntityFrancois) || params->param1 < getState()->time || params->param4)
+ && !params->param5
+ && getData()->entityPosition < getEntityData(kEntityMmeBoutarel)->entityPosition) {
+
+ if (getData()->direction == kDirectionDown) {
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction202221040);
+ params->param4 = 1;
+ params->param5 = 1;
+ } else if (params->param4 && getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityMmeBoutarel, 1000)) {
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction168986720);
+ params->param5 = 1;
+ }
+ }
+ } else if (params->param1 < getState()->time) {
+ getData()->clothes = kClothesDefault;
+ getData()->field_4A3 = 30;
+ getData()->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(kEntityFrancois))
+ getSound()->processEntry(kEntityFrancois);
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(kEntityFrancois))
+ getSound()->processEntry(kEntityFrancois);
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisWhistle);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventFrancoisWhistle)
+ && !getEvent(kEventFrancoisWhistleD)
+ && !getEvent(kEventFrancoisWhistleNight)
+ && !getEvent(kEventFrancoisWhistleNightD))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->clothes = kClothes1;
+ getData()->field_4A3 = 100;
+ getData()->inventoryItem = kItemNone;
+
+ params->param2 = kCarGreenSleeping;
+ params->param3 = kPosition_540;
+
+ getEntities()->updateEntity(kEntityFrancois, kCarGreenSleeping, kPosition_540);
+
+ params->param6 = 15 * rnd(7);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityFrancois, kEntityCoudert, kAction168253822);
+ // Fallback to next case
+
+ case 3:
+ params->param2 = kCarRedSleeping;
+ params->param3 = kPosition_9460;
+ params->param5 = 0;
+
+ getData()->entityPosition = kPosition_2088;
+
+ getEntities()->updateEntity(kEntityFrancois, kCarRedSleeping, kPosition_9460);
+ goto label_callback;
+
+ case 4:
+ setCallback(5);
+ setup_function10();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ if (getProgress().jacket == kJacketGreen) {
+ if (isNight())
+ getAction()->playAnimation(getData()->entityPosition <= getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisWhistleNightD : kEventFrancoisWhistleNight);
+ else
+ getAction()->playAnimation(getData()->entityPosition <= getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisWhistleD : kEventFrancoisWhistleD);
+ }
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750 * (getData()->direction == kDirectionUp ? -1 : 1)), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+
+ case kAction102752636:
+ getEntities()->clearSequences(kEntityFrancois);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ getData()->clothes = kClothesDefault;
+ getData()->field_4A3 = 30;
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction205346192:
+ if (savepoint.entity2 == kEntityCoudert)
+ params->param8 = 1;
+ else if (savepoint.entity2 == kEntityMertens)
+ params->param7 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Francois, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(675);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(675);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function10();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Francois, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("605Df", kObjectCompartment6);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+
+ setCallback(5);
+ setup_playSound("Har2010");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityFrancois, kEntityAlouan, kAction189489753);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4840);
+ break;
+
+ case 7:
+ if (getInventory()->hasItem(kItemWhistle) || getInventory()->get(kItemWhistle)->location == kObjectLocation3) {
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityFrancois, "605He");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateFromTime(450);
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityFrancois, kObjectCompartmentE, true);
+
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function10();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction190219584:
+ setCallback(6);
+ setup_enterExitCompartment("605Ef", kObjectCompartment6);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(14, Francois, function14, ObjectIndex, EntityPosition)
+ // Expose parameters as IISS and ignore the default exposed parameters
+ EntityData::EntityParametersIISS *parameters = (EntityData::EntityParametersIISS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ strcpy((char *)&parameters->seq2, "605H");
+ strcat((char *)&parameters->seq2, (char *)&parameters->seq1);
+
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, (EntityPosition)parameters->param2);
+ break;
+
+ case 2:
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ getEntities()->drawSequenceLeft(kEntityFrancois, (char *)&parameters->seq2);
+ getEntities()->enterCompartment(kEntityFrancois, (ObjectIndex)parameters->param1, true);
+
+ setCallback(3);
+ setup_playSound("Fra2005A");
+ } else {
+ if (parameters->param2 >= kPosition_5790) {
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ } else {
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ }
+ }
+ break;
+
+ case 3:
+ case 5:
+ setCallback(getCallback() + 1);
+ setup_updateFromTime(rnd(450));
+ break;
+
+ case 4:
+ case 6:
+ setCallback(getCallback() + 1);
+ setup_playSound(rnd(2) ? "Fra2005B" : "Fra2005C");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(rnd(150));
+ break;
+
+ case 8:
+ getEntities()->exitCompartment(kEntityFrancois, (ObjectIndex)parameters->param1);
+ // Fallback to next case
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_updateFromTime(900);
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function10();
+ break;
+
+ case 13:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Francois, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getData()->entityPosition >= getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ } else {
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ }
+ break;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function10();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ if (!getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound("Fra2012");
+ break;
+
+ case 8:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Francois, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityBoutarel)->entityPosition;
+ getData()->location = getEntityData(kEntityBoutarel)->location;
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_enterExitCompartment("605Cd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_5890;
+
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction101107728);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityFrancois);
+ getSavePoints()->push(kEntityFrancois, kEntityBoutarel, kAction237889408);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("605Id", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction100957716);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Francois, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Francois, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTimeParisEpernay, params->param1, 1, setup_function11, kTime1093500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function19();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Francois, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1161000, params->param1, 2, setup_function12);
+ break;
+
+ case kAction101107728:
+ setCallback(1);
+ setup_function16();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Francois, function20)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityFrancois);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Francois, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Francois, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("605Id", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction100957716);
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+ setup_function23();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Francois, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventFrancoisShowBeetle) || getEvent(kEventFrancoisShowBeetleD))
+ if (!getEvent(kEventFrancoisTradeWhistle) && !getEvent(kEventFrancoisTradeWhistleD))
+ ENTITY_PARAM(0, 1) = 1;
+
+ if (ENTITY_PARAM(0, 1) && getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ setCallback(1);
+ setup_function15();
+ break;
+ }
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTime1764000, params->param1, 2, setup_playSound, "Fra2011");
+
+label_callback_2:
+ TIME_CHECK_CALLBACK(kTime1800000, params->param2, 3, setup_function13);
+
+label_callback_3:
+ if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ TIME_CHECK_CALLBACK_1(kTime1768500, params->param3, 4, setup_function11, kTime1773000);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime1827000, params->param4, 5, setup_function11, kTime1831500);
+ }
+
+label_callback_5:
+ if (getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ // TODO: do we also need to check if the whistle is in the inventory?
+ break;
+ }
+
+ if (params->param5 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(kTimeEnd, !getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000), params->param5, 75);
+ setCallback(6);
+ setup_playSound("Fra2010");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_3(kTime1782000, params->param6, 7, setup_function14, kObjectCompartmentC, kPosition_6470, "c");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_3(kTime1813500, params->param7, 8, setup_function14, kObjectCompartmentF, kPosition_4070, "f");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ getProgress().field_9C = 1;
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Francois, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Francois, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventFrancoisShowBeetle) || getEvent(kEventFrancoisShowBeetleD))
+ if (!getEvent(kEventFrancoisTradeWhistle) && !getEvent(kEventFrancoisTradeWhistleD))
+ ENTITY_PARAM(0, 1) = 1;
+
+ if (params->param2 && getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790) && !params->param1) {
+
+ if (ENTITY_PARAM(0, 1) && getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ setCallback(2);
+ setup_function15();
+ break;
+ }
+
+label_callback_2:
+ TIME_CHECK_CALLBACK(kTime2025000, params->param3, 3, setup_function12);
+
+label_callback_3:
+ TIME_CHECK_CALLBACK(kTime2052000, params->param4, 4, setup_function12);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK(kTime2079000, params->param5, 5, setup_function12);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2092500, params->param6, 6, setup_function12);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK(kTime2173500, params->param7, 7, setup_function12);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2182500, params->param8, 8, setup_function12);
+
+label_callback_8:
+ TIME_CHECK_CALLBACK(kTime2241000, CURRENT_PARAM(1, 1), 9, setup_function12);
+
+label_callback_9:
+ if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ TIME_CHECK_CALLBACK_1(kTime2011500, CURRENT_PARAM(1, 2), 10, setup_function11, kTime2016000);
+
+label_callback_10:
+ TIME_CHECK_CALLBACK_1(kTime2115000, CURRENT_PARAM(1, 3), 11, setup_function11, kTime2119500);
+ }
+
+label_callback_11:
+ if (getInventory()->get(kItemWhistle)->location == kObjectLocation3) {
+ if (getState()->time <= kTimeEnd)
+ if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000) || !params->param4)
+ params->param4 = (uint)(getState()->time + 75);
+
+ if (params->param4 < getState()->time || getState()->time > kTimeEnd) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(12);
+ setup_playSound("Fra2010");
+ break;
+ }
+
+label_callback_12:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 5), 13, setup_function14, kObjectCompartmentE, kPosition_4840, "e");
+
+label_callback_13:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 6), 14, setup_function14, kObjectCompartmentF, kPosition_4070, "f");
+
+label_callback_14:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 7), 15, setup_function14, kObjectCompartmentB, kPosition_7500, "b");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param2 = 1;
+ break;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ getProgress().field_9C = 1;
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kAction189872836:
+ params->param1 = 1;
+ break;
+ case kAction190390860:
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Francois, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Francois, chapter4Handler)
+ if (savepoint.action == kAction101107728) {
+ setCallback(1);
+ setup_function16();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Francois, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Francois, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5) {
+ if (!getInventory()->hasItem(kItemWhistle)
+ && getInventory()->get(kItemWhistle)->location != kObjectLocation3)
+ getInventory()->setLocationAndProcess(kItemWhistle, kObjectLocation1);
+
+ setup_function30();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Francois, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(31, Francois)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/francois.h b/engines/lastexpress/entities/francois.h
new file mode 100644
index 0000000000..c924cf677b
--- /dev/null
+++ b/engines/lastexpress/entities/francois.h
@@ -0,0 +1,170 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_FRANCOIS_H
+#define LASTEXPRESS_FRANCOIS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Francois : public Entity {
+public:
+ Francois(LastExpressEngine *engine);
+ ~Francois() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION_1(function11, TimeValue timeValue)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION_3(function14, ObjectIndex compartment, EntityPosition entityPosition, const char *str)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function30)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FRANCOIS_H
diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp
new file mode 100644
index 0000000000..620a885a60
--- /dev/null
+++ b/engines/lastexpress/entities/gendarmes.cpp
@@ -0,0 +1,620 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/gendarmes.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Gendarmes::Gendarmes(LastExpressEngine *engine) : Entity(engine, kEntityGendarmes) {
+ ADD_CALLBACK_FUNCTION(Gendarmes, reset);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter1);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestDraw);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestPlaysound);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestPlaysound16);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestCallback);
+ ADD_CALLBACK_FUNCTION(Gendarmes, savegame);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestUpdateEntity);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function9);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function10);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function12);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function13);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter2);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter3);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter4);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Gendarmes, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Gendarmes, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Gendarmes, arrestDraw)
+ arrest(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Gendarmes, arrestPlaysound)
+ arrest(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(5, Gendarmes, arrestPlaysound16)
+ arrest(savepoint, true, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Gendarmes, arrestCallback, uint32)
+ arrest(savepoint, true, SoundManager::kFlagInvalid, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Gendarmes, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Gendarmes, arrestUpdateEntity, CarIndex, EntityPosition)
+ arrest(savepoint, true, SoundManager::kFlagInvalid, false, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition)
+ EntityData::EntityParametersSSS *parameters1 = (EntityData::EntityParametersSSS*)_data->getCurrentParameters(1);
+ EntityData::EntityParametersISII *parameters2 = (EntityData::EntityParametersISII*)_data->getCurrentParameters(2);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (params->param2 <= kPosition_3050) {
+ if (params->param2 != kPosition_3050) {
+ if (params->param2 == kPosition_2740)
+ parameters2->param5 = kObjectCompartment8;
+ } else {
+ parameters2->param5 = kObjectCompartment7;
+ parameters2->param6 = true;
+ }
+ } else if (params->param2 <= kPosition_4840) {
+ if (params->param2 != kPosition_4840) {
+ if (params->param2 == kPosition_4070) {
+ parameters2->param5 = kObjectCompartment6;
+ parameters2->param7 = kPosition_4455;
+ }
+ } else {
+ parameters2->param5 = kObjectCompartment5;
+ parameters2->param6 = true;
+ parameters2->param7 = kPosition_4455;
+ }
+ } else if (params->param2 <= kPosition_6470) {
+ if (params->param2 != kPosition_6470) {
+ if (params->param2 == kPosition_5790) {
+ parameters2->param5 = kObjectCompartment4;
+ parameters2->param7 = kPosition_6130;
+ }
+ } else {
+ parameters2->param5 = kObjectCompartment3;
+ parameters2->param6 = true;
+ parameters2->param7 = kPosition_6130;
+ }
+ } else if (params->param2 != kPosition_7500) {
+ if (params->param2 == kPosition_8200) {
+ parameters2->param5 = kObjectCompartment1;
+ parameters2->param6 = true;
+ parameters2->param7 = kPosition_7850;
+ }
+ } else {
+ parameters2->param5 = kObjectCompartment2;
+ parameters2->param7 = kPosition_7850;
+ }
+
+ if (params->param1 == kCarBaggageRear)
+ parameters2->param5 += 31; // Switch to next compartment car
+
+ if (parameters2->param6) {
+ strcpy((char *)&parameters1->seq1, "632A");
+ strcpy((char *)&parameters1->seq2, "632B");
+ strcpy((char *)&parameters1->seq3, "632C");
+ } else {
+ strcpy((char *)&parameters1->seq1, "632D");
+ strcpy((char *)&parameters1->seq2, "632E");
+ strcpy((char *)&parameters1->seq3, "632F");
+ }
+
+ strcat((char *)&parameters1->seq1, (char *)&params->seq1);
+ strcat((char *)&parameters1->seq2, (char *)&params->seq1);
+ strcat((char *)&parameters1->seq3, (char *)&params->seq1);
+
+ if ((getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)params->param2)
+ || getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)parameters2->param7)
+ || (params->param1 == kCarGreenSleeping && params->param2 == kPosition_8200 && getEntities()->isOutsideAlexeiWindow()))
+ && !getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ setCallback(1);
+ setup_function10((CarIndex)params->param1, (EntityPosition)params->param2, (ObjectIndex)parameters2->param5);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityGendarmes, (char *)&parameters1->seq1);
+ getEntities()->enterCompartment(kEntityGendarmes, (ObjectIndex)CURRENT_PARAM(2, 5));
+
+ setCallback(parameters2->param6 ? 2 : 3);
+ setup_arrestPlaysound(parameters2->param6 ? "POL1044A" : "POL1044B");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ CALLBACK_ACTION();
+ break;
+
+ case 2:
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityGendarmes, (char *)&parameters1->seq2);
+ if (getEntities()->isNobodyInCompartment((CarIndex)params->param1, (EntityPosition)params->param2) || !strcmp(params->seq2, "NODIALOG")) {
+ setCallback(4);
+ setup_arrestCallback(150);
+ } else {
+ char *arrestSound = (char *)&parameters2->seq;
+ strcpy(arrestSound, "POL1045");
+ strcat(arrestSound, (char *)&params->seq2);
+
+ setCallback(5);
+ setup_arrestPlaysound(arrestSound);
+ }
+ break;
+
+ case 4:
+ case 5:
+ if (!getEntities()->isNobodyInCompartment((CarIndex)params->param1, (EntityPosition)params->param2) && strcmp(params->seq2, "NODIALOG")) {
+ char *arrestSound = (char *)&parameters2->seq;
+ strcpy(arrestSound, "POL1043");
+ strcat(arrestSound, (char *)&params->seq2);
+
+ getSound()->playSound(kEntityGendarmes, arrestSound, SoundManager::kFlagInvalid, 30);
+ }
+
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_arrestDraw((char *)&parameters1->seq3);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->exitCompartment(kEntityGendarmes, (ObjectIndex)parameters2->param5);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param5 || getState()->timeTicks > (uint32)params->param5) {
+ if (!params->param5)
+ params->param5 = getState()->timeTicks + 75;
+
+ if (!getEntities()->isOutsideAlexeiWindow() && getObjects()->get((ObjectIndex)params->param3).location != kObjectLocation1) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ break;
+ }
+ }
+
+ if (!params->param6)
+ params->param6 = getState()->timeTicks + 150;
+
+ if (params->param6 == 0 || getState()->timeTicks > (uint32)params->param6) {
+ params->param6 = kTimeInvalid;
+
+ getSound()->playSound(kEntityGendarmes, "POL1046A", SoundManager::kFlagDefault);
+ }
+
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 300);
+
+ if (!params->param4 && getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ } else {
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityGendarmes, "LIB017", SoundManager::kFlagDefault);
+
+ setCallback(getProgress().jacket == kJacketBlood ? 3 : 4);
+ setup_savegame(kSavegameTypeEvent, getProgress().jacket == kJacketBlood ? kEventMertensBloodJacket : kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionKnock:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_arrestPlaysound16("POL1046B");
+ break;
+
+ case kActionOpenDoor:
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ break;
+
+ case kActionDefault:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_arrestPlaysound16("POL1046");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorTalk, kCursorNormal);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityGendarmes, "LIB014", SoundManager::kFlagDefault);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 3:
+ getAction()->playAnimation((params->param1 < kCarRedSleeping) ? kEventMertensBloodJacket : kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorHand);
+ break;
+
+ case 6:
+ getSound()->playSound(kEntityGendarmes, "LIB014", SoundManager::kFlagDefault);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Gendarmes, chapter1Handler)
+ if (savepoint.action == kAction169499649) {
+ getSavePoints()->push(kEntityGendarmes, kEntityMertens, kAction190082817);
+ setup_function12();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Gendarmes, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getProgress().field_14 = 29;
+
+ setCallback(1);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_5540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function9(kCarGreenSleeping, kPosition_5790, "d", "A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_6220);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function9(kCarGreenSleeping, kPosition_6470, "c", "B");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_7250);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function9(kCarGreenSleeping, kPosition_7500, "b", "C");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_7950);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function9(kCarGreenSleeping, kPosition_8200, "a", "NODIALOG");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 9:
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping) {
+ getProgress().field_14 = 0;
+ getEntities()->clearSequences(kEntityGendarmes);
+ getSavePoints()->push(kEntityGendarmes, kEntityVerges, kAction168710784);
+ setup_function13();
+ break;
+ }
+
+ setCallback(10);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_2490);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function9(kCarRedSleeping, kPosition_2740, "h", "NODIALOG");
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_3820);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function9(kCarRedSleeping, kPosition_4070, "f", "E");
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_4590);
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function9(kCarRedSleeping, kPosition_4840, "e", "F");
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_5540);
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_function9(kCarRedSleeping, kPosition_5790, "d", "G");
+ break;
+
+ case 17:
+ setCallback(18);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_6220);
+ break;
+
+ case 18:
+ setCallback(19);
+ setup_function9(kCarRedSleeping, kPosition_6470, "c", "H");
+ break;
+
+ case 19:
+ setCallback(20);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_7250);
+ break;
+
+ case 20:
+ setCallback(21);
+ setup_function9(kCarRedSleeping, kPosition_7500, "b", "J");
+ break;
+
+ case 21:
+ setCallback(22);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_7950);
+ break;
+
+ case 22:
+ setCallback(23);
+ setup_function9(kCarRedSleeping, kPosition_8200, "a", "NODIALOG");
+ break;
+
+ case 23:
+ setCallback(24);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 24:
+ getProgress().field_14 = 0;
+ getEntities()->clearSequences(kEntityGendarmes);
+ getSavePoints()->push(kEntityGendarmes, kEntityVerges, kAction168710784);
+ setup_function13();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Gendarmes, function13)
+ if (savepoint.action == kActionDefault)
+ getData()->car = kCarNone;
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Gendarmes, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Gendarmes, chapter3)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Gendarmes, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Gendarmes, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundManager::FlagType flag, bool checkCallback, bool shouldUpdateEntity) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (checkCallback) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ TIME_CHECK_CALLBACK_ACTION(params->param1, params->param2);
+ }
+
+ if (shouldUpdateEntity) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ // Fallback to next action
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(0, 1) && getEntities()->hasValidFrame(kEntityGendarmes)) {
+ getSound()->playSound(kEntityPlayer, "MUS007");
+ ENTITY_PARAM(0, 1) = 1;
+ }
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityGendarmes, kEntityPlayer, 1000) && !getEntityData(kEntityPlayer)->location) {
+
+ if (shouldUpdateEntity)
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 22) && !getEntities()->isDistanceBetweenEntities(kEntityGendarmes, kEntityPlayer, 250))
+ break;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ // Only handle when passing SIIS params
+ if (!checkCallback) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ if (!shouldPlaySound)
+ getEntities()->drawSequenceRight(kEntityGendarmes, (char *)&params->seq1);
+ else
+ getSound()->playSound(kEntityGendarmes, (char *)&params->seq1, flag);
+ }
+
+ if (shouldUpdateEntity) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/gendarmes.h b/engines/lastexpress/entities/gendarmes.h
new file mode 100644
index 0000000000..095a74fa44
--- /dev/null
+++ b/engines/lastexpress/entities/gendarmes.h
@@ -0,0 +1,99 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_GENDARMES_H
+#define LASTEXPRESS_GENDARMES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+#include "lastexpress/game/sound.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Gendarmes : public Entity {
+public:
+ Gendarmes(LastExpressEngine *engine);
+ ~Gendarmes() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_1(arrestDraw, const char *sequence)
+ DECLARE_FUNCTION_1(arrestPlaysound, const char *soundName)
+ DECLARE_FUNCTION_1(arrestPlaysound16, const char *soundName)
+ DECLARE_FUNCTION_1(arrestCallback, uint32 timeValue)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION_2(arrestUpdateEntity, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_4(function9, CarIndex car, EntityPosition entityPosition, const char *sequence1, const char *sequence2)
+ DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, ObjectIndex object)
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+private:
+ void arrest(const SavePoint &savepoint, bool playSound = false, SoundManager::FlagType flag = SoundManager::kFlagInvalid, bool checkCallback = false, bool shouldUpdateEntity = false);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_GENDARMES_H
diff --git a/engines/lastexpress/entities/hadija.cpp b/engines/lastexpress/entities/hadija.cpp
new file mode 100644
index 0000000000..5590c1b6fe
--- /dev/null
+++ b/engines/lastexpress/entities/hadija.cpp
@@ -0,0 +1,532 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/hadija.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Hadija::Hadija(LastExpressEngine *engine) : Entity(engine, kEntityHadija) {
+ ADD_CALLBACK_FUNCTION(Hadija, reset);
+ ADD_CALLBACK_FUNCTION(Hadija, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Hadija, playSound);
+ ADD_CALLBACK_FUNCTION(Hadija, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Hadija, updateEntity);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment6);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment8);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment6to8);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment8to6);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter1);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function12);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter2);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter3);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter4);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function19);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter5);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function22);
+ ADD_CALLBACK_FUNCTION(Hadija, function23);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Hadija, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Hadija, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Hadija, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Hadija, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Hadija, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Hadija, compartment6)
+ COMPARTMENT_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Cf", "619Df");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Hadija, compartment8)
+ COMPARTMENT_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Hadija, compartment6to8)
+ COMPARTMENT_FROM_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Hadija, compartment8to6)
+ COMPARTMENT_FROM_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Hadija, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Hadija, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime1084500, params->param2, 2, setup_compartment6to8);
+
+label_callback2:
+ if (params->param3 != kTimeInvalid && getState()->time > kTime1093500) {
+
+ if (getState()->time <= kTime1134000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !getEntities()->isInsideCompartment(kEntityMahmud, kCarGreenSleeping, kPosition_5790) || !params->param3) {
+ params->param3 = (uint)getState()->time + 75;
+
+ if (!params->param3) {
+ setCallback(3);
+ setup_compartment8();
+ return;
+ }
+ }
+
+ if (params->param3 >= getState()->time)
+ return;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ setCallback(3);
+ setup_compartment8();
+ }
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime1156500, params->param4, 4, setup_compartment8to6);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime1165500) {
+ if (getState()->time <= kTime1188000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !getEntities()->isInsideCompartment(kEntityMahmud, kCarGreenSleeping, kPosition_5790) || !params->param5) {
+ params->param5 = (uint)getState()->time + 75;
+
+ if (!params->param5) {
+ setCallback(5);
+ setup_compartment6();
+ return;
+ }
+ }
+
+ if (params->param5 >= getState()->time)
+ return;
+ }
+
+ params->param5 = kTimeInvalid;
+
+ setCallback(5);
+ setup_compartment6();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Hadija, function12)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityHadija);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Hadija, chapter2)
+ if (savepoint.action == kActionDefault) {
+
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1782000 && !params->param1) {
+ params->param1 = 1;
+ getData()->entityPosition = kPosition_2740;
+ }
+
+ if (params->param2 == kTimeInvalid || getState()->time <= kTime1786500) {
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+ }
+
+ if (getState()->time <= kTime1818000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2)
+ params->param2 = (uint)getState()->time + 75;
+
+ if (params->param2 >= getState()->time) {
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+ }
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_compartment8();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("Har2012");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Hadija, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Hadija, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1998000, params->param1, 1, setup_compartment6to8);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2020500, params->param2, 2, setup_compartment8to6);
+
+label_callback2:
+ TIME_CHECK_CALLBACK(kTime2079000, params->param3, 3, setup_compartment6to8);
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime2187000, params->param4, 4, setup_compartment8to6);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime2196000)
+ TIME_CHECK_CAR(kTime2254500, params->param5, 5, setup_compartment6);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Hadija, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Hadija, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid)
+ TIME_CHECK_CAR(kTime1714500, params->param1, 1, setup_compartment6);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2367000, params->param2, 2, setup_compartment6to8);
+
+label_callback2:
+ TIME_CHECK_CALLBACK(kTime2421000, params->param3, 3, setup_compartment8to6);
+
+label_callback3:
+ if (params->param4 != kTimeInvalid && getState()->time > kTime2425500)
+ TIME_CHECK_CAR(kTime2484000, params->param4, 4, setup_compartment6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Hadija, function19)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityHadija);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Hadija, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Hadija, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function22();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Hadija, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setup_function23();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Hadija, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("619AF", kObjectCompartment6);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(24, Hadija)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/hadija.h b/engines/lastexpress/entities/hadija.h
new file mode 100644
index 0000000000..bd37a205d9
--- /dev/null
+++ b/engines/lastexpress/entities/hadija.h
@@ -0,0 +1,144 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_HADIJA_H
+#define LASTEXPRESS_HADIJA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Hadija : public Entity {
+public:
+ Hadija(LastExpressEngine *engine);
+ ~Hadija() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(compartment6)
+ DECLARE_FUNCTION(compartment8)
+ DECLARE_FUNCTION(compartment6to8)
+ DECLARE_FUNCTION(compartment8to6)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_HADIJA_H
diff --git a/engines/lastexpress/entities/ivo.cpp b/engines/lastexpress/entities/ivo.cpp
new file mode 100644
index 0000000000..6bee62f003
--- /dev/null
+++ b/engines/lastexpress/entities/ivo.cpp
@@ -0,0 +1,829 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/ivo.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Ivo::Ivo(LastExpressEngine *engine) : Entity(engine, kEntityIvo) {
+ ADD_CALLBACK_FUNCTION(Ivo, reset);
+ ADD_CALLBACK_FUNCTION(Ivo, draw);
+ ADD_CALLBACK_FUNCTION(Ivo, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Ivo, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Ivo, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Ivo, updateEntity);
+ ADD_CALLBACK_FUNCTION(Ivo, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Ivo, playSound);
+ ADD_CALLBACK_FUNCTION(Ivo, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Ivo, savegame);
+ ADD_CALLBACK_FUNCTION(Ivo, function11);
+ ADD_CALLBACK_FUNCTION(Ivo, sitAtTableWithSalko);
+ ADD_CALLBACK_FUNCTION(Ivo, leaveTableWithSalko);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter1);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, function16);
+ ADD_CALLBACK_FUNCTION(Ivo, function17);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter2);
+ ADD_CALLBACK_FUNCTION(Ivo, function19);
+ ADD_CALLBACK_FUNCTION(Ivo, function20);
+ ADD_CALLBACK_FUNCTION(Ivo, function21);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter3);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter4);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, function26);
+ ADD_CALLBACK_FUNCTION(Ivo, function27);
+ ADD_CALLBACK_FUNCTION(Ivo, function28);
+ ADD_CALLBACK_FUNCTION(Ivo, function29);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter5);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, fight);
+ ADD_CALLBACK_FUNCTION(Ivo, function33);
+ ADD_CALLBACK_FUNCTION(Ivo, function34);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Ivo, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Ivo, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Ivo, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Ivo, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(5, Ivo, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(6, Ivo, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath || savepoint.action == kActionExcuseMe) {
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ return;
+ }
+
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Ivo, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(8, Ivo, playSound)
+ Entity::playSound(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Ivo, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Ivo, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Ivo, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityIvo, kEntitySalko, 750) || getEntities()->checkDistanceFromPosition(kEntitySalko, kPosition_2740, 500)) {
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction123668192);
+
+ setCallback(4);
+ setup_enterExitCompartment("613Ah", kObjectCompartmentH);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "809DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityIvo);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ if (getEntities()->isDistanceBetweenEntities(kEntityIvo, kEntitySalko, 750) || getEntities()->checkDistanceFromPosition(kEntitySalko, kPosition_2740, 500)) {
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction123668192);
+
+ setCallback(3);
+ setup_enterExitCompartment("613Ah", kObjectCompartmentH);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Hh");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH, true);
+ }
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Ivo, sitAtTableWithSalko)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntitySalko);
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "023A1");
+ getEntities()->drawSequenceRight(kEntitySalko, "023A2");
+ getEntities()->drawSequenceRight(kEntityTables2, "023A3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Ivo, leaveTableWithSalko)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kActionDrawTablesWithChairs, "009E");
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "023D1");
+ getEntities()->drawSequenceRight(kEntitySalko, "023D2");
+ getEntities()->drawSequenceRight(kEntityTables2, "023D3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Ivo, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject47, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Ivo, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityMilos)->entityPosition;
+ getData()->location = getEntityData(kEntityMilos)->location;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function11();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityIvo, kEntityMilos, kAction135024800);
+ setup_function16();
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_updateFromTicks(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Ivo, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityIvo);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Ch");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH);
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction88652208);
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("613Bh", kObjectCompartmentH);
+ break;
+
+ case kAction123852928:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+
+ setCallback(2);
+ setup_enterExitCompartment("613Dh", kObjectCompartmentH);
+ break;
+
+ case kAction221683008:
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Ivo, function17)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Ivo, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1777500, params->param1, setup_function19);
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject47, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Ivo, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("613FH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction136184016);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("809US");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_sitAtTableWithSalko();
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function20();
+ break;
+ }
+ break;
+
+ case kAction102675536:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Ivo, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1809000 && params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_leaveTableWithSalko();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction189688608);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction101106391);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ params->param1 = 1;
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction236237423);
+ setup_function21();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityIvo, "023C2");
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Ivo, function21)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Ivo, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Ivo, chapter3Handler)
+ if (savepoint.action == kActionDefault)
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Ivo, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Ivo, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2361600 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ setup_function26();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Ivo, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_leaveTableWithSalko();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function11();
+ break;
+
+ case 2:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Ivo, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityIvo);
+ setup_function28();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Ch");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction88652208);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ setCallback(1);
+ setup_enterExitCompartment("613FH", kObjectCompartmentH);
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_enterExitCompartment("613Bh", kObjectCompartmentH);
+ break;
+
+ case kAction123852928:
+ setCallback(4);
+ setup_enterExitCompartment("613Dh", kObjectCompartmentH);
+ break;
+
+ case kAction221683008:
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Ivo, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2425500 && !params->param1) {
+ params->param1 = 1;
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("613EH", kObjectCompartmentH);
+ break;
+
+ case 2:
+ setup_function29();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Ivo, function29)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Ivo, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggageRear;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Ivo, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_fight();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Ivo, fight)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_540;
+ getData()->car = kCarBaggageRear;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathIvoFight);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "LIB090");
+ getAction()->playAnimation(kEventCathIvoFight);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightIvo);
+ if (params->param1) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 96);
+ setup_function33();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Ivo, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getState()->time = (TimeValue)(getState()->time + 1800);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getObjects()->update(kObject94, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+
+ case kAction135800432:
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Ivo, function34)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityIvo);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/ivo.h b/engines/lastexpress/entities/ivo.h
new file mode 100644
index 0000000000..e726c95af0
--- /dev/null
+++ b/engines/lastexpress/entities/ivo.h
@@ -0,0 +1,177 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_IVO_H
+#define LASTEXPRESS_IVO_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Ivo : public Entity {
+public:
+ Ivo(LastExpressEngine *engine);
+ ~Ivo() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(sitAtTableWithSalko)
+ DECLARE_FUNCTION(leaveTableWithSalko)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(fight)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_IVO_H
diff --git a/engines/lastexpress/entities/kahina.cpp b/engines/lastexpress/entities/kahina.cpp
new file mode 100644
index 0000000000..89c685cfe9
--- /dev/null
+++ b/engines/lastexpress/entities/kahina.cpp
@@ -0,0 +1,1528 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/kahina.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Kahina::Kahina(LastExpressEngine *engine) : Entity(engine, kEntityKahina) {
+ ADD_CALLBACK_FUNCTION(Kahina, reset);
+ ADD_CALLBACK_FUNCTION(Kahina, playSound);
+ ADD_CALLBACK_FUNCTION(Kahina, savegame);
+ ADD_CALLBACK_FUNCTION(Kahina, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Kahina, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Kahina, function6);
+ ADD_CALLBACK_FUNCTION(Kahina, updateEntity2);
+ ADD_CALLBACK_FUNCTION(Kahina, updateEntity);
+ ADD_CALLBACK_FUNCTION(Kahina, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter1);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, function12);
+ ADD_CALLBACK_FUNCTION(Kahina, function13);
+ ADD_CALLBACK_FUNCTION(Kahina, function14);
+ ADD_CALLBACK_FUNCTION(Kahina, function15);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter2);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter3);
+ ADD_CALLBACK_FUNCTION(Kahina, function19);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, function21);
+ ADD_CALLBACK_FUNCTION(Kahina, function22);
+ ADD_CALLBACK_FUNCTION(Kahina, function23);
+ ADD_CALLBACK_FUNCTION(Kahina, function24);
+ ADD_CALLBACK_FUNCTION(Kahina, function25);
+ ADD_CALLBACK_FUNCTION(Kahina, function26);
+ ADD_CALLBACK_FUNCTION(Kahina, function27);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter4);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Kahina, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Kahina, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Kahina, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Kahina, updateFromTime, uint32)
+ if (savepoint.action == kAction137503360) {
+ ENTITY_PARAM(0, 2) = 1;
+ CALLBACK_ACTION();
+ }
+
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Kahina, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ params->param2 = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_updateEntity2(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_updateEntity2(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 0;
+
+ setCallback(1);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityKahina);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(5);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityKahina);
+ break;
+ }
+ break;
+
+ case kAction137503360:
+ ENTITY_PARAM(0, 2) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Kahina, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ ENTITY_PARAM(0, 1) = 1;
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kAction137503360:
+ ENTITY_PARAM(0, 2) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Kahina, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1019" : "CAT1019A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(9, Kahina, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Kahina, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Kahina, chapter1Handler)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (getProgress().jacket != kJacketOriginal)
+ TIME_CHECK_SAVEPOINT(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837);
+
+ if (getProgress().eventMertensKronosInvitation)
+ setup_function12();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Kahina, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1485000, params->param2, setup_function13);
+ break;
+
+ case kActionKnock:
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ // Fallback to next action
+
+ case kActionOpenDoor:
+ if (!getEvent(kEventKronosGoingToInvitation)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosGoingToInvitation);
+ break;
+ }
+
+ if (savepoint.action == kActionOpenDoor)
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 80);
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction171849314);
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosGoingToInvitation);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80);
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction171849314);
+ params->param1 = 1;
+ }
+ break;
+
+ case kAction137685712:
+ setup_function13();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Kahina, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 || getState()->time >= kTime1201500 || params->param2 == kTimeInvalid || params->param1 >= getState()->time)
+ break;
+
+ if (getState()->time <= kTime1197000) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2) {
+ params->param2 = (uint)getState()->time;
+
+ if (!getState()->time)
+ goto label_callback;
+ }
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+label_callback:
+ setCallback(1);
+ setup_function15();
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = (uint)getState()->time + 1800;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Kahina, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF);
+ CALLBACK_ACTION();
+ break;
+
+ case kAction4:
+ getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityKahina, "616Cf");
+ getEntities()->enterCompartment(kEntityKahina, kObjectCompartmentF);
+ getSavePoints()->push(kEntityKahina, kEntityMax, kAction158007856);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Kahina, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param2, 0)
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ UPDATE_PARAM_PROC_END
+ }
+ break;
+
+ case kActionDefault:
+ getProgress().field_14 = 19;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->hasValidFrame(kEntityKahina)) {
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isOutsideAlexeiWindow()
+ || getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 2000)) {
+ if (getProgress().field_14 == 19)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ } else {
+ setCallback(5);
+ setup_enterExitCompartment("616Aa", kObjectCompartment1);
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(7);
+ setup_enterExitCompartment("616Ba", kObjectCompartment1);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getProgress().field_14 == 19)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 8:
+ getEntities()->clearSequences(kEntityKahina);
+ params->param1 = (uint)getState()->time + 4500;
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function14();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 11:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_6130)) {
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ } else {
+ setCallback(12);
+ setup_enterExitCompartment("616Ac", kObjectCompartmentC);
+ }
+ break;
+
+ case 12:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, getObjects()->get(kObjectCompartmentC).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityPlayer, getObjects()->get(kObject50).location, kCursorNormal, kCursorNormal);
+
+ setCallback(13);
+ setup_updateFromTime(900);
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, getObjects()->get(kObjectCompartmentC).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, getObjects()->get(kObject50).location, kCursorHandKnock, kCursorHand);
+
+ setCallback(14);
+ setup_enterExitCompartment("616Bc", kObjectCompartmentC);
+ break;
+
+ case 14:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 15:
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(16);
+ setup_updateFromTime(900);
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 17:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Kahina, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Kahina, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 9000)
+ params->param1 = 1;
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEvent(kEventKahinaAskSpeakFirebird) && getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 900)
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_3:
+ if (getState()->time > kTime1845000 && getEvent(kEventKronosConversationFirebird) && getEntities()->isInKronosSalon(kEntityPlayer)) {
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getEvent(kEventKronosConversationFirebird))
+ break;
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)) {
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getEvent(kEventTatianaGivePoem) || getEvent(kEventTatianaBreakfastGivePoem)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaAskSpeakFirebird);
+ break;
+ }
+
+ if (params->param1) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getAction()->playAnimation(kEventKahinaAskSpeak);
+ getScenes()->processScene();
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(8);
+ setup_playSound("KRO3003");
+ } else {
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 9 : 10);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(getCallback() + 1);
+ setup_updateFromTime(900);
+ break;
+
+ case 2:
+ case 5:
+ setCallback(getCallback() + 1);
+ setup_playSound("KRO3005");
+ break;
+
+ case 3:
+ goto label_callback_3;
+
+ case 7:
+ getAction()->playAnimation(kEventKahinaAskSpeakFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityKahina, "KRO3004");
+ break;
+
+ case 8:
+ case 9:
+ case 10:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ if (getCallback() == 8)
+ params->param1 = 0;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Kahina, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventAnnaBaggageArgument))
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function22);
+
+ if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1019" : "CAT1019A");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityKahina);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Kahina, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventKronosVisit))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4);
+ break;
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2079000 && !params->param2) {
+ params->param2 = 1;
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)
+ && !getEvent(kEventKronosConversationFirebird)
+ && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+label_callback_2:
+ if (getEntities()->isInKronosSalon(kEntityPlayer))
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+
+ setup_function21();
+ break;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 9000)
+ params->param1 = 1;
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)
+ && !getEvent(kEventKronosConversationFirebird)
+ && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ UPDATE_PARAM(params->param4, getState()->time, 900);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getEvent(kEventKronosConversationFirebird)) {
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getEvent(kEventTatianaGivePoem) || getEvent(kEventTatianaBreakfastGivePoem)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaAskSpeakFirebird);
+ break;
+ }
+
+ if (params->param1) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getAction()->playAnimation(kEventKahinaAskSpeak);
+ getScenes()->processScene();
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(10);
+ setup_playSound("KRO3003");
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 11 : 12);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ if (getEvent(kEventKronosConversationFirebird)) {
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ } else {
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, false);
+ goto label_callback_1;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ goto label_callback_2;
+
+ case 3:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(4);
+ setup_updateFromTime(900);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("KRO3005");
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_playSound("KRO3005");
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventKahinaAskSpeakFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityKahina, "KRO3004");
+ break;
+
+ case 10:
+ params->param1 = 0;
+ // Fallback to next case
+
+ case 11:
+ case 12:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Kahina, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (!params->param3)
+ params->param3 = (uint)getState()->time + 4500;
+
+ if (params->param6 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param5, 0)
+ setCallback(2);
+ setup_function23();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+label_callback_2:
+ if (params->param2) {
+
+ if (!params->param4)
+ params->param4 = (uint)getState()->time + 4500;
+
+ if (params->param6 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param6, 0)
+ getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina));
+ getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina));
+
+ getEntities()->drawSequenceLeft(kEntityKahina, "202a");
+
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (!getProgress().field_44
+ && getState()->time > kTime2214000) {
+
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+
+ if (location == kObjectLocation3 || location == kObjectLocation7) {
+ setCallback(3);
+ setup_function25();
+ } else if (location == kObjectLocation1 || location == kObjectLocation2) {
+ setCallback(4);
+ setup_function26();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityKahina, "202a");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 2:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback_2;
+ }
+ break;
+
+ case kAction92186062:
+ if (params->param1) {
+ setCallback(1);
+ setup_function23();
+ }
+ break;
+
+ case kAction134611040:
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ setup_function24();
+ break;
+
+ case kAction137503360:
+ setup_function22();
+ break;
+
+ case kAction237555748:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Kahina, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+
+ if (ENTITY_PARAM(0, 3) || location == kObjectLocation3 || location == kObjectLocation7) {
+ setCallback(1);
+ setup_function25();
+ } else if (location == kObjectLocation2 || location == kObjectLocation1) {
+ setCallback(2);
+ setup_function26();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+
+ case kActionDrawScene:
+ if (getData()->car > kCarGreenSleeping || (getData()->car == kCarGreenSleeping && getData()->entityPosition > kPosition_2740))
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Kahina, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina));
+ getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina), 15);
+
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_540;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_4455) || getEntities()->isOutsideAnnaWindow()) {
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("616Cf", kObjectCompartmentF);
+ }
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, getObjects()->get(kObjectCompartmentF).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityPlayer, getObjects()->get(kObject53).location, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_updateFromTime(900);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, getObjects()->get(kObjectCompartmentF).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, getObjects()->get(kObject53).location, kCursorHandKnock, kCursorHand);
+
+ setCallback(4);
+ setup_enterExitCompartment("616Df", kObjectCompartmentF);
+ break;
+
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 7:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Kahina, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param2, (EntityPosition)params->param3)) {
+ getEntities()->clearSequences(kEntityKahina);
+ params->param1 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos))
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kActionOpenDoor);
+ else
+ setup_function27();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function6(kTime2241000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 2)) {
+ getEntities()->clearSequences(kEntityKahina);
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ getProgress().field_44 = 0;
+
+ setup_function22();
+ } else if (ENTITY_PARAM(0, 1)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaGunYellow);
+ } else {
+ setup_function27();
+ }
+ break;
+
+ case 2:
+ if (getEntityData(kEntityPlayer)->entityPosition >= getData()->entityPosition)
+ getAction()->playAnimation(getData()->car < kCarRedSleeping ? kEventKahinaGunYellow : kEventKahinaGunBlue);
+ else
+ getAction()->playAnimation(kEventKahinaGun);
+
+ getEntities()->updateEntity(kEntityKahina, kCarKronos, kPosition_9270);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction235599361);
+ getSound()->playSound(kEntityKahina, "MUS016", SoundManager::kFlagDefault);
+ getProgress().field_44 = 1;
+
+ params->param1 = true;
+ params->param2 = kCarKronos;
+ params->param3 = kPosition_9270;
+ break;
+ }
+ break;
+
+ case kAction137503360:
+ getEntities()->clearSequences(kEntityKahina);
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ getProgress().field_44 = 0;
+
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Kahina, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime2263500) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param1)
+ params->param1 = (uint)getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ setCallback(12);
+ setup_enterExitCompartment("616Ba", kObjectCompartment1);
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAnnaBaggageArgument)) {
+ setCallback(1);
+ setup_function19(kCarGreenSleeping, kPosition_8200);
+ break;
+ }
+
+ switch (getInventory()->get(kItemFirebird)->location) {
+ default:
+ break;
+
+ case kObjectLocation3:
+ case kObjectLocation7:
+ if (getInventory()->get(kItemFirebird)->location == kObjectLocation3)
+ getProgress().field_7C = 1;
+ else
+ getProgress().field_80 = 1;
+
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ break;
+ }
+
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ getProgress().field_78 = 1;
+ ENTITY_PARAM(0, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping)) {
+ setCallback(getCallback() + 1);
+ setup_function19(getCallback() == 1 ? kCarGreenSleeping : kCarKronos, getCallback() == 1 ? kPosition_9460 : kPosition_9270);
+ break;
+ } else {
+ if (getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850) || getEntities()->isOutsideAlexeiWindow()) {
+ setCallback(6);
+ setup_playSound("LIB013");
+ } else {
+ setCallback(8);
+ setup_enterExitCompartment("616Aa", kObjectCompartment1);
+ }
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 5:
+ case 7:
+ case 11:
+ case 13:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function19(kCarKronos, kPosition_9270);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(9);
+ setup_updateFromTime(900);
+ break;
+
+ case 9:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (getInventory()->get(kItemFirebird)->location) {
+ default:
+ if (ENTITY_PARAM(0, 3))
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ break;
+
+ case kObjectLocation3:
+ case kObjectLocation7:
+ if (getInventory()->get(kItemFirebird)->location == kObjectLocation3)
+ getProgress().field_7C = 1;
+ else
+ getProgress().field_80 = 1;
+
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ getProgress().field_C0 = (uint)getState()->time;
+ getProgress().field_78 = 1;
+ break;
+ }
+
+ getProgress().field_78 = 1;
+ ENTITY_PARAM(0, 3) = 0;
+
+ if (getInventory()->get(kItemFirebird)->location != kObjectLocation18) {
+ setCallback(10);
+ setup_enterExitCompartment("616Ba", kObjectCompartment1);
+ }
+ break;
+
+ case 10:
+ case 12:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(getCallback() + 1);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Kahina, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAnnaBaggageArgument)) {
+ setCallback(1);
+ setup_function19(kCarRedSleeping, kPosition_8200);
+ break;
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ getProgress().field_78 = 1;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850)) {
+ setCallback(2);
+ setup_function19(kCarRedSleeping, kPosition_9460);
+ } else {
+ setCallback(6);
+ setup_enterExitCompartment("616Aa", kObjectCompartmentA);
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case 4:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850)) {
+ setCallback(5);
+ setup_function19(kCarRedSleeping, kPosition_9270);
+ } else {
+ setCallback(6);
+ setup_enterExitCompartment("616Aa", kObjectCompartmentA);
+ }
+ break;
+
+ case 5:
+ case 9:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, getObjects()->get(kObjectCompartmentA).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, getObjects()->get(kObject48).location, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, getObjects()->get(kObjectCompartmentA).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityPlayer, getObjects()->get(kObject48).location, kCursorHandKnock, kCursorHand);
+
+ if (getInventory()->get(kItemFirebird)->location == kObjectLocation1 || getInventory()->get(kItemFirebird)->location == kObjectLocation2) {
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ setCallback(8);
+ setup_enterExitCompartment("616Ba", kObjectCompartmentA);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(9);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Kahina, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer))
+ params->param1 = kEventKahinaPunchCar;
+ else if (getEntities()->isPlayerInCar(kCarGreenSleeping))
+ params->param1 = kEventKahinaPunchBlue;
+ else if (getEntities()->isPlayerInCar(kCarRedSleeping))
+ params->param1 = kEventKahinaPunchYellow;
+ else if (getEntities()->isInSalon(kEntityPlayer))
+ params->param1 = kEventKahinaPunchSalon;
+ else if (getEntities()->isInRestaurant(kEntityPlayer))
+ params->param1 = kEventKahinaPunchRestaurant;
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ params->param1 = kEventKahinaPunchKitchen;
+ else if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ params->param1 = kEventKahinaPunchBaggageCarEntrance;
+ else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage))
+ params->param1 = kEventKahinaPunchBaggageCar;
+
+ if (params->param1) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kSceneGameOverAlarm2);
+ }
+ break;
+
+ case kActionDefault:
+ getState()->timeDelta = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation((EventIndex)params->param1);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Kahina, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKahina);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Kahina, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKahina);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/kahina.h b/engines/lastexpress/entities/kahina.h
new file mode 100644
index 0000000000..4be9d9fb27
--- /dev/null
+++ b/engines/lastexpress/entities/kahina.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_KAHINA_H
+#define LASTEXPRESS_KAHINA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Kahina : public Entity {
+public:
+ Kahina(LastExpressEngine *engine);
+ ~Kahina() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ DECLARE_FUNCTION_1(function6, TimeValue timeValue)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Update the entity, handling excuse me events and resetting the entity state after the argument with Anna in the baggage car
+ *
+ * @param car The car index
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function19, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_KAHINA_H
diff --git a/engines/lastexpress/entities/kronos.cpp b/engines/lastexpress/entities/kronos.cpp
new file mode 100644
index 0000000000..3335edb2fb
--- /dev/null
+++ b/engines/lastexpress/entities/kronos.cpp
@@ -0,0 +1,892 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/kronos.h"
+
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/kahina.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tatiana.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+static const struct {
+ uint32 time;
+ const char *sequence;
+} concertData[54] = {
+ {735, "201d"}, {1395, "201a"}, {1965, "201d"}, {2205, "201a"}, {3405, "201d"},
+ {3750, "201a"}, {3975, "201d"}, {4365, "201a"}, {4650, "201d"}, {4770, "201a"},
+ {4995, "201e"}, {5085, "201d"}, {5430, "201a"}, {5685, "201d"}, {5850, "201a"},
+ {7515, "201d"}, {7620, "201a"}, {7785, "201d"}, {7875, "201a"}, {8235, "201d"},
+ {8340, "201a"}, {8745, "201d"}, {8805, "201a"}, {8925, "201d"}, {8985, "201a"},
+ {9765, "201d"}, {9930, "201a"}, {12375, "201e"}, {12450, "201d"}, {12705, "201c"},
+ {13140, "201d"}, {13305, "201a"}, {13380, "201d"}, {13560, "201a"}, {14145, "201d"},
+ {14385, "201a"}, {14445, "201c"}, {14805, "201a"}, {16485, "201d"}, {16560, "201a"},
+ {16755, "201d"}, {16845, "201a"}, {17700, "201d"}, {17865, "201a"}, {18645, "201d"},
+ {18720, "201a"}, {19410, "201e"}, {19500, "201a"}, {22020, "201d"}, {22185, "201a"},
+ {22590, "201d"}, {22785, "201a"}, {23085, "201d"}, {23265, "201a"}
+};
+
+Kronos::Kronos(LastExpressEngine *engine) : Entity(engine, kEntityKronos) {
+ ADD_CALLBACK_FUNCTION(Kronos, reset);
+ ADD_CALLBACK_FUNCTION(Kronos, savegame);
+ ADD_CALLBACK_FUNCTION(Kronos, updateEntity);
+ ADD_CALLBACK_FUNCTION(Kronos, playSound);
+ ADD_CALLBACK_FUNCTION(Kronos, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Kronos, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter1);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Kronos, function9);
+ ADD_CALLBACK_FUNCTION(Kronos, function10);
+ ADD_CALLBACK_FUNCTION(Kronos, function11);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter2);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter3);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Kronos, function15);
+ ADD_CALLBACK_FUNCTION(Kronos, function16);
+ ADD_CALLBACK_FUNCTION(Kronos, function17);
+ ADD_CALLBACK_FUNCTION(Kronos, function18);
+ ADD_CALLBACK_FUNCTION(Kronos, function19);
+ ADD_CALLBACK_FUNCTION(Kronos, function20);
+ ADD_CALLBACK_FUNCTION(Kronos, function21);
+ ADD_CALLBACK_FUNCTION(Kronos, function22);
+ ADD_CALLBACK_FUNCTION(Kronos, function23);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter4);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Kronos, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(2, Kronos, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Kronos, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Kronos, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Kronos, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(6, Kronos, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Kronos, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Kronos, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1489500, params->param2, setup_function11);
+ break;
+
+ case kAction171849314:
+ params->param1 = 1;
+ break;
+
+ case kAction202621266:
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Kronos, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversation);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosConversation);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction137685712);
+ setup_function10();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Kronos, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1489500, params->param1, setup_function11);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ getEntities()->clearSequences(kEntityKronos);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Kronos, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ params->param1++;
+ getSound()->playSound(kEntityKronos, (params->param1 & 1) ? "KRO1001" : "KRO1002");
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7000;
+
+ if (!getSound()->isBuffered(kEntityKronos))
+ getSound()->playSound(kEntityKronos, "KRO1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Kronos, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Kronos, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKronos);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Kronos, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1993500 && !params->param1 && !params->param2 && !params->param3)
+ setup_function15();
+ break;
+
+ case kAction157159392:
+ switch (savepoint.entity2) {
+ case kEntityAnna:
+ params->param1 = 1;
+ break;
+
+ case kEntityTatiana:
+ params->param2 = 1;
+ break;
+
+ case kEntityAbbot:
+ params->param3 = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Kronos, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && !getEntities()->isInSalon(kEntityBoutarel)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 75)
+ setup_function16();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3 != kTimeInvalid && getState()->time > kTime2002500) {
+ if (getState()->time <= kTime2052000) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time + 900;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setup_function16();
+ } else {
+ getSavePoints()->push(kEntityKronos, kEntityAnna, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityTatiana, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityAbbot, kAction101169422);
+
+ setup_function18();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 60)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 59)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 83)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 81)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 87))
+ params->param1 = 1;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isPlayerPosition(kCarRestaurant, 51) && !getEntities()->isInSalon(kEntityBoutarel))
+ setup_function16();
+ else
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 60)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 59)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 83)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 81)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 87);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Kronos, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosVisit);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosVisit);
+ getSavePoints()->push(kEntityKronos, kEntityAnna, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityTatiana, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityAbbot, kAction101169422);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 60);
+
+ setup_function17();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Kronos, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9270);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function18();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Kronos, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2079000 && !params->param2) {
+ getObjects()->updateLocation2(kObjectCompartmentKronos, kObjectLocation3);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param1 = 1;
+ params->param2 = 1;
+ }
+
+ TIME_CHECK(kTime2106000, params->param3, setup_function19)
+ else {
+ if (params->param1 && getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->car = kCarKronos;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Kronos, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+
+ case kActionDrawScene:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventConcertStart);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityKronos);
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function39);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_chaptersHandler);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function50);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function56);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function35);
+
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Kronos, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ params->param5 = getSound()->getEntryTime(kEntityKronos)* 2;
+
+ if (params->param6 < ARRAYSIZE(concertData) && params->param5 > concertData[params->param6].time) {
+
+ getEntities()->drawSequenceLeft(kEntityKronos, concertData[params->param6].sequence);
+
+ if (scumm_stricmp(concertData[params->param6].sequence, "201e")) {
+
+ if (scumm_stricmp(concertData[params->param6].sequence, "201c")) {
+
+ if (!scumm_stricmp(concertData[params->param6].sequence, "201d")) {
+ if (getEntities()->isPlayerPosition(kCarKronos, 86))
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 86);
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 85);
+ } else {
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 85);
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 86);
+ }
+ } else {
+ if (getEntities()->isPlayerPosition(kCarKronos, 85))
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 85);
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 86);
+ }
+ } else {
+ if (getEntities()->isPlayerPosition(kCarKronos, 85) || getEntities()->isPlayerPosition(kCarKronos, 86))
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 85);
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 86);
+ }
+
+ ++params->param6;
+ }
+
+ getObjects()->update(kObject76, kEntityKronos, kObjectLocationNone, kCursorNormal, getInventory()->hasItem(kItemBriefcase) ? kCursorHand : kCursorNormal);
+
+ if (!params->param7) {
+ params->param7 = (uint)getState()->time + 2700;
+ params->param8 = (uint)getState()->time + 13500;
+ }
+
+ if (CURRENT_PARAM(1, 2) != kTimeInvalid && params->param7 < getState()->time) {
+ UPDATE_PARAM_PROC_TIME(params->param8, !params->param1, CURRENT_PARAM(1, 2), 450)
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction237555748);
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!params->param1)
+ params->param2 = params->param3;
+
+ params->param2 -= getState()->timeDelta;
+
+ if (params->param2 < getState()->timeDelta) {
+
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction92186062);
+
+ ++params->param4;
+ switch (params->param4) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCathWakingUp);
+ getScenes()->processScene();
+ params->param3 = 1800;
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventCathWakingUp);
+ getScenes()->processScene();
+ params->param3 = 3600;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathFallingAsleep);
+
+ while (getSound()->isBuffered("1919.LNK"))
+ getSound()->updateQueue();
+
+ getAction()->playAnimation(kEventCathWakingUp);
+ getScenes()->processScene();
+ params->param3 = 162000;
+ break;
+ }
+ params->param2 = params->param3;
+ }
+
+ if (params->param5 > 23400 || CURRENT_PARAM(1, 1)) {
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (CURRENT_PARAM(1, 1)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 26);
+
+ setup_function21();
+ break;
+ }
+
+ if (getEntities()->isInKronosSalon(kEntityPlayer)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventConcertEnd);
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ getSound()->playSound(kEntityKronos, "Kro3001");
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ CURRENT_PARAM(1, 1) = 1;
+ break;
+ }
+
+ setup_function21();
+ break;
+
+ case kActionOpenDoor:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventConcertLeaveWithBriefcase);
+ break;
+
+ case kActionDefault:
+ getState()->time = kTime2115000;
+ getState()->timeDelta = 3;
+
+ params->param1 = (getEntities()->isPlayerPosition(kCarKronos, 88)
+ || getEntities()->isPlayerPosition(kCarKronos, 84)
+ || getEntities()->isPlayerPosition(kCarKronos, 85)
+ || getEntities()->isPlayerPosition(kCarKronos, 86)
+ || getEntities()->isPlayerPosition(kCarKronos, 83));
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+ else
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getObjects()->update(kObject76, kEntityKronos, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_40 = 1;
+ getEntities()->drawSequenceLeft(kEntityKronos, "201a");
+
+ params->param2 = 2700;
+ params->param3 = 2700;
+ break;
+
+ case kActionDrawScene:
+ params->param1 = (getEntities()->isPlayerPosition(kCarKronos, 88)
+ || getEntities()->isPlayerPosition(kCarKronos, 84)
+ || getEntities()->isPlayerPosition(kCarKronos, 85)
+ || getEntities()->isPlayerPosition(kCarKronos, 86)
+ || getEntities()->isPlayerPosition(kCarKronos, 83));
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ else
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_6000;
+ getAction()->playAnimation(kEventConcertLeaveWithBriefcase);
+
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function21);
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventConcertEnd);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 26);
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Kronos, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getProgress().field_40 = 0;
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ getSavePoints()->push(kEntityKronos, kEntityRebecca, kAction191668032);
+ if (!getEvent(kEventConcertLeaveWithBriefcase))
+ setup_function22();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ }
+ break;
+
+ case kAction235599361:
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Kronos, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_44) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchBaggageCarEntrance);
+ } else {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getSound()->isBuffered(savepoint.action == kActionKnock ? "LIB012" : "LIB013", true))
+ getSound()->playSound(kEntityPlayer, savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ getSavePoints()->call(kEntityKronos, kEntityKahina, kAction137503360);
+
+ if (getInventory()->hasItem(kItemBriefcase)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosReturnBriefcase);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird) && getEvent(kEventConcertLeaveWithBriefcase)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringEggCeiling);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringEggCeiling);
+ break;
+ }
+
+ if (getEvent(kEventConcertLeaveWithBriefcase)) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringNothing);
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKronos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKronosReturnBriefcase);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->removeItem(kItemScarf);
+
+ setup_function23();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosBringEggCeiling);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+
+ setup_function23();
+ break;
+
+ case 3:
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getAction()->playAnimation(kEventKronosBringEgg);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->addItem(kItemBriefcase);
+ setup_function23();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventKronosBringNothing);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ break;
+ }
+ break;
+
+ case kAction138085344:
+ setup_function23();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Kronos, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Kronos, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Kronos, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/kronos.h b/engines/lastexpress/entities/kronos.h
new file mode 100644
index 0000000000..f73c245347
--- /dev/null
+++ b/engines/lastexpress/entities/kronos.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_KRONOS_H
+#define LASTEXPRESS_KRONOS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Kronos : public Entity {
+public:
+ Kronos(LastExpressEngine *engine);
+ ~Kronos() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_KRONOS_H
diff --git a/engines/lastexpress/entities/mahmud.cpp b/engines/lastexpress/entities/mahmud.cpp
new file mode 100644
index 0000000000..0b4dc1b138
--- /dev/null
+++ b/engines/lastexpress/entities/mahmud.cpp
@@ -0,0 +1,839 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/entities/mahmud.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Mahmud::Mahmud(LastExpressEngine *engine) : Entity(engine, kEntityMahmud) {
+ ADD_CALLBACK_FUNCTION(Mahmud, reset);
+ ADD_CALLBACK_FUNCTION(Mahmud, draw);
+ ADD_CALLBACK_FUNCTION(Mahmud, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Mahmud, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Mahmud, playSound);
+ ADD_CALLBACK_FUNCTION(Mahmud, playSoundMertens);
+ ADD_CALLBACK_FUNCTION(Mahmud, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Mahmud, savegame);
+ ADD_CALLBACK_FUNCTION(Mahmud, updateEntity);
+ ADD_CALLBACK_FUNCTION(Mahmud, function10);
+ ADD_CALLBACK_FUNCTION(Mahmud, function11);
+ ADD_CALLBACK_FUNCTION(Mahmud, function12);
+ ADD_CALLBACK_FUNCTION(Mahmud, function13);
+ ADD_CALLBACK_FUNCTION(Mahmud, chaptersHandler);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter1);
+ ADD_CALLBACK_FUNCTION(Mahmud, resetChapter);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter2);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter3);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter4);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Mahmud, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(2, Mahmud, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Mahmud, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param7, getState()->timeTicks, params->param5);
+
+ if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
+ getScenes()->loadSceneFromObject((ObjectIndex)params->param6, true);
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityMahmud, (ObjectIndex)params->param4);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityMahmud, (char *)&params->seq);
+ getEntities()->enterCompartment(kEntityMahmud, (ObjectIndex)params->param4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(5, Mahmud, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Mahmud, playSoundMertens)
+ Entity::playSound(savepoint, false, getSound()->getSoundFlag(kEntityMertens));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Mahmud, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Mahmud, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Mahmud, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getInventory()->hasItem(kItemPassengerList))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1025" : "CAT1025Q");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param6, getState()->time, 13500);
+
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterExitCompartment("614Ed", kObjectCompartment4);
+ break;
+
+ case kActionEndSound:
+ case kActionDrawScene:
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ EntityPosition position = getEntityData(kEntityPlayer)->entityPosition;
+ if (position < kPosition_1500 || position >= kPosition_5790 || (position > kPosition_4455 && params->param5 != 5)) {
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_enterExitCompartment("614Ed", kObjectCompartment4);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getSound()->isBuffered((savepoint.action == kActionKnock) ? "LIB012" : "LIB013", true))
+ getSound()->playSound(kEntityPlayer, (savepoint.action == kActionKnock) ? "LIB012" : "LIB013");
+
+ params->param5 = savepoint.param.intValue;
+
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ params->param3++;
+
+ switch(params->param3) {
+ default:
+ params->param4 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMahmud, "MAH1174");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMahmud, "MAH1173B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMahmud, params->param2 ? "MAH1170E" : "MAH1173A");
+ break;
+ }
+ }
+
+ if (params->param4) {
+ if (getState()->time >= kTimeCityGalanta) {
+ params->param3 = 0;
+ } else {
+ getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, (getProgress().chapter == kChapter1) ? kSceneGameOverPolice1 : kSceneGameOverPolice2, true);
+ }
+ break;
+ }
+
+ getAction()->handleOtherCompartment((ObjectIndex)savepoint.param.intValue, false, false);
+
+ switch (getScenes()->get(getState()->scene)->position) {
+ default:
+ break;
+
+ case 55:
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ break;
+
+ case 56:
+ getScenes()->loadSceneFromObject(kObjectCompartment6, true);
+ break;
+
+ case 57:
+ getScenes()->loadSceneFromObject(kObjectCompartment7, true);
+ break;
+
+ case 58:
+ getScenes()->loadSceneFromObject(kObjectCompartment8, true);
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMahmud, params->param2 ? "MAH1170A" : "MAH1173", SoundManager::kFlagInvalid, 45);
+ getProgress().field_C4 = 1;
+
+ setCallback(1);
+ setup_enterExitCompartment2("614Dd", kObjectCompartment4, 30, (ObjectIndex)params->param1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment5, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Md");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+ break;
+
+ case 2:
+ case 3:
+ getEntities()->exitCompartment(kEntityMahmud, kObjectCompartment4, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Mahmud, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor: {
+ getSound()->playSound(kEntityPlayer, (savepoint.action == kActionKnock ? "LIB012" : "LIB013"));
+
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ params->param1++;
+
+ getSound()->playSound(kEntityMahmud, (params->param1 == 1 ? "MAH1170E" : (params->param1 == 2 ? "MAH1173B" : "MAH1174")));
+ }
+
+ switch (getScenes()->get(getState()->scene)->position) {
+ default:
+ break;
+
+ case 55:
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ break;
+
+ case 56:
+ getScenes()->loadSceneFromObject(kObjectCompartment6, true);
+ break;
+
+ case 57:
+ getScenes()->loadSceneFromObject(kObjectCompartment7, true);
+ break;
+
+ case 58:
+ getScenes()->loadSceneFromObject(kObjectCompartment8, true);
+ break;
+ }
+ break;
+ }
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction102227384);
+ setCallback(1);
+ setup_enterExitCompartment("614Ad", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Kd");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+
+ setCallback(2);
+ setup_playSound("MAH1170A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSoundMertens("MAH1170B");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("MAH1170C");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSoundMertens("MAH1170D");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound("MAH1170E");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSoundMertens("MAH1170F");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("614Ld", kObjectCompartment4);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Bd");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+
+ setCallback(9);
+ setup_playSound("MAH1170G");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_playSoundMertens("MAH1170H");
+ break;
+
+ case 10:
+ getObjects()->update(kObjectCompartment5, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 11:
+ getEntities()->exitCompartment(kEntityMahmud, kObjectCompartment4, true);
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityMahmud);
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction123852928:
+ if (getSound()->isBuffered(kEntityMahmud))
+ getSound()->processEntry(kEntityMahmud);
+
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(11);
+ setup_enterExitCompartment("614Cd", kObjectCompartment4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// TODO: factorize code between function12 & function13
+IMPLEMENT_FUNCTION(12, Mahmud, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("614Gd", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("614Ff", kObjectCompartment6);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ setCallback(4);
+ setup_playSound("Har1105");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("614Gf", kObjectCompartment6);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("614Fd", kObjectCompartment4);
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Mahmud, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("614Gd", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("614Fh", kObjectCompartment8);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ setCallback(4);
+ setup_playSound("Har1107");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("614Gh", kObjectCompartment8);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("614Fd", kObjectCompartment4);
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1)) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction204379649);
+ ENTITY_PARAM(0, 1) = 0;
+ }
+
+ if (!params->param2 && getProgress().chapter == kChapter1) {
+
+ TIME_CHECK_CALLBACK(kTime1098000, params->param6, 1, setup_function13);
+
+ if (!getSound()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) {
+ params->param7 = 1;
+
+ setCallback(2);
+ setup_function12();
+ break;
+ }
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param4 = 1;
+ params->param5 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorNormal, kCursorNormal);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param5) {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (getProgress().jacket == kJacketBlood || getEvent(kEventMahmudWrongDoor) || getEvent(kEventMahmudWrongDoorOriginalJacket) || getEvent(kEventMahmudWrongDoorDay)) {
+ // Check if we have the passenger list
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(6);
+ setup_playSound(rnd(2) == 0 ? "CAT1501" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(7);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 8 : 9);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ } else {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityMahmud);
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param4 || params->param5) {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ return;
+
+ case 1:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+
+ if (!getSound()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) {
+ params->param7 = 1;
+ setCallback(2);
+ setup_function12();
+ break;
+ }
+
+ params->param8 = 0;
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ params->param8 = 0;
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("MAH1175");
+ break;
+
+ case 5: {
+ CursorStyle cursor = kCursorHand;
+ CursorStyle cursor2 = kCursorHandKnock;
+
+ if (getProgress().jacket == kJacketBlood
+ || getEvent(kEventMahmudWrongDoor)
+ || getEvent(kEventMahmudWrongDoorOriginalJacket)
+ || getEvent(kEventMahmudWrongDoorDay)) {
+ cursor = kCursorNormal;
+ cursor2 = kCursorTalk;
+ }
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation1, cursor, cursor2);
+ params->param5 = 1;
+ break;
+ }
+
+ case 6:
+ case 7:
+ params->param4 = 1;
+ break;
+
+ case 8:
+ case 9:
+ setCallback(10);
+ setup_savegame(kSavegameTypeEvent, kEventMahmudWrongDoor);
+ return;
+
+ case 10:
+ getAction()->playAnimation((getProgress().jacket == kJacketGreen) ? (isNight() ? kEventMahmudWrongDoor : kEventMahmudWrongDoorDay) : kEventMahmudWrongDoorOriginalJacket);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->processScene();
+
+ params->param4 = 1;
+ break;
+
+ case 11:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ break;
+
+ case 12:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param2 = 0;
+ params->param4 = 0;
+ params->param5 = 0;
+ break;
+ }
+ break;
+
+ case kAction225563840:
+ setCallback(12);
+ setup_function11();
+ break;
+
+ case kAction290410610:
+ params->param3 = (params->param3 < 1) ? 1 : 0;
+ setCallback(11);
+ setup_function10((ObjectIndex)savepoint.param.intValue, (bool)params->param3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Mahmud, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMahmud, kAction170483072, 0);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject20, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Mahmud, resetChapter)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityMahmud);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Mahmud, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Mahmud, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Mahmud, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Mahmud, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityMahmud);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mahmud.h b/engines/lastexpress/entities/mahmud.h
new file mode 100644
index 0000000000..647d48b8ed
--- /dev/null
+++ b/engines/lastexpress/entities/mahmud.h
@@ -0,0 +1,153 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_MAHMUD_H
+#define LASTEXPRESS_MAHMUD_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Mahmud : public Entity {
+public:
+ Mahmud(LastExpressEngine *engine);
+ ~Mahmud() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param ticks The time ticks
+ * @param object The object for loading the scene
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment2, const char *sequence, ObjectIndex compartment, uint32 ticks, ObjectIndex object)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSoundMertens, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_2(function10, ObjectIndex, bool)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Handle chapters events
+ */
+ DECLARE_FUNCTION(chaptersHandler)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Reset chapter data
+ */
+ DECLARE_FUNCTION(resetChapter)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MAHMUD_H
diff --git a/engines/lastexpress/entities/max.cpp b/engines/lastexpress/entities/max.cpp
new file mode 100644
index 0000000000..a846f7b6dd
--- /dev/null
+++ b/engines/lastexpress/entities/max.cpp
@@ -0,0 +1,628 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/max.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Max::Max(LastExpressEngine *engine) : Entity(engine, kEntityMax) {
+ ADD_CALLBACK_FUNCTION(Max, reset);
+ ADD_CALLBACK_FUNCTION(Max, playSound);
+ ADD_CALLBACK_FUNCTION(Max, draw);
+ ADD_CALLBACK_FUNCTION(Max, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Max, savegame);
+ ADD_CALLBACK_FUNCTION(Max, chapter12_handler);
+ ADD_CALLBACK_FUNCTION(Max, function7);
+ ADD_CALLBACK_FUNCTION(Max, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Max, function9);
+ ADD_CALLBACK_FUNCTION(Max, chapter1);
+ ADD_CALLBACK_FUNCTION(Max, chapter2);
+ ADD_CALLBACK_FUNCTION(Max, chapter3);
+ ADD_CALLBACK_FUNCTION(Max, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Max, freeFromCage);
+ ADD_CALLBACK_FUNCTION(Max, function15);
+ ADD_CALLBACK_FUNCTION(Max, chapter4);
+ ADD_CALLBACK_FUNCTION(Max, function17);
+ ADD_CALLBACK_FUNCTION(Max, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Max, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Max, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(3, Max, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Max, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Max, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Max, chapter12_handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ break;
+
+ case kAction71277948:
+ setCallback(1);
+ setup_function7();
+ break;
+
+ case kAction158007856:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Max, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param2 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ setCallback((savepoint.action == kActionKnock) ? 1 : 2);
+ setup_playSound((savepoint.action == kActionKnock) ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 56) || getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getSound()->playSound(kEntityMax, "Max1120");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ case 0:
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("Max1122");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction101687594:
+ getEntities()->clearSequences(kEntityMax);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction122358304:
+ case kActionMaxFreeFromCage:
+ getSavePoints()->push(kEntityMax, kEntityMax, kActionMaxFreeFromCage);
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction158007856:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Max, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param2);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3101");
+
+ params->param2 = 255 * (4 * rnd(20) + 40);
+ params->param3 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (params->param1) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxLickHand);
+ break;
+ }
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getAction()->playAnimation(kEventCathMaxLickHand);
+ getScenes()->processScene();
+
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ params->param2 = 255 * (4 * rnd(20) + 40);
+
+ getObjects()->update(kObjectCageMax, kEntityMax, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3101");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getSound()->playSound(kEntityPlayer, "LIB026");
+ getAction()->playAnimation(kEventCathMaxFree);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 92);
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Max, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid || !getState()->time)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->hasValidFrame(kEntityMax) || !params->param2) {
+
+ params->param2 = (uint)getState()->time;
+ if (!params->param2)
+ goto setup_functions;
+ }
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+setup_functions:
+ if (getProgress().chapter == kChapter3)
+ setup_function15();
+
+ if (getProgress().chapter == kChapter4)
+ setup_function17();
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw Max outside of cage
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getEntities()->enterCompartment(kEntityMax, kObjectCompartmentF, true);
+
+ params->param1 = (uint)(getState()->time + 2700);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Max, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter12_handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Max, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter12_handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Max, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Max, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ break;
+ }
+
+ UPDATE_PARAM(params->param3, getState()->time, params->param1);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction71277948:
+ setCallback(1);
+ setup_function7();
+ break;
+
+ case kAction122358304:
+ params->param2 = 1;
+ break;
+
+ case kActionMaxFreeFromCage:
+ setup_freeFromCage();
+ break;
+
+ case kAction158007856:
+ if (params->param2)
+ break;
+
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Max, freeFromCage)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityMax, "Max1122");
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Save game after freeing Max from his cage
+ case kActionOpenDoor:
+ if (getEvent(kEventCathMaxCage)) {
+ if (getEvent(kEventCathMaxFree)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxFree);
+ }
+
+ } else {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxCage);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCageMax, kEntityMax, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Play animation for Max in the cage and after opening it
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->removeFromQueue(kEntityMax);
+
+ getAction()->playAnimation(kEventCathMaxCage);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityMax);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getSound()->playSound(kEntityPlayer, "LIB026");
+ getAction()->playAnimation(kEventCathMaxFree);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 92);
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ setup_function9();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Max, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ getData()->car = getEntityData(kEntityCoudert)->car;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM(params->param3, getState()->time, 900);
+
+ getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3010");
+
+ setCallback(1);
+ setup_enterExitCompartment("630Bf", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getEntities()->enterCompartment(kEntityMax, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityMax, kEntityAnna, kAction156622016);
+ }
+ break;
+
+ case kAction122358304:
+ (savepoint.entity2 == kEntityAnna) ? (params->param1 = 1) : (params->param2 = 1);
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ getEntities()->drawSequenceLeft(kEntityMax, "BLANK");
+ break;
+
+ case kActionMaxFreeFromCage:
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ setup_chapter4Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Max, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Max, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ getData()->car = getEntityData(kEntityCoudert)->car;
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693);
+ break;
+
+ case kAction122358304:
+ params->param1 = 1;
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ getEntities()->drawSequenceLeft(kEntityMax, "BLANK");
+ break;
+
+ case kActionMaxFreeFromCage:
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ setup_chapter4Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Max, chapter5)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/max.h b/engines/lastexpress/entities/max.h
new file mode 100644
index 0000000000..93eb165a0f
--- /dev/null
+++ b/engines/lastexpress/entities/max.h
@@ -0,0 +1,129 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_MAX_H
+#define LASTEXPRESS_MAX_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Max : public Entity {
+public:
+ Max(LastExpressEngine *engine);
+ ~Max() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Handle Chapter 1 & 2 events
+ */
+ DECLARE_FUNCTION(chapter12_handler)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(freeFromCage)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MAX_H
diff --git a/engines/lastexpress/entities/mertens.cpp b/engines/lastexpress/entities/mertens.cpp
new file mode 100644
index 0000000000..d204c204f1
--- /dev/null
+++ b/engines/lastexpress/entities/mertens.cpp
@@ -0,0 +1,4113 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/mertens.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+#define SAVEGAME_BLOOD_JACKET() \
+ if (getProgress().jacket == kJacketBlood \
+ && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000) \
+ && !getEntities()->isInsideCompartments(kEntityPlayer) \
+ && !getEntities()->checkFields10(kEntityPlayer)) { \
+ setCallback(1); \
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \
+ }
+
+Mertens::Mertens(LastExpressEngine *engine) : Entity(engine, kEntityMertens) {
+ ADD_CALLBACK_FUNCTION(Mertens, reset);
+ ADD_CALLBACK_FUNCTION(Mertens, bloodJacket);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(Mertens, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Mertens, playSound);
+ ADD_CALLBACK_FUNCTION(Mertens, playSound16);
+ ADD_CALLBACK_FUNCTION(Mertens, savegame);
+ ADD_CALLBACK_FUNCTION(Mertens, updateEntity);
+ ADD_CALLBACK_FUNCTION(Mertens, function11);
+ ADD_CALLBACK_FUNCTION(Mertens, bonsoir);
+ ADD_CALLBACK_FUNCTION(Mertens, function13);
+ ADD_CALLBACK_FUNCTION(Mertens, function14);
+ ADD_CALLBACK_FUNCTION(Mertens, function15);
+ ADD_CALLBACK_FUNCTION(Mertens, function16);
+ ADD_CALLBACK_FUNCTION(Mertens, function17);
+ ADD_CALLBACK_FUNCTION(Mertens, function18);
+ ADD_CALLBACK_FUNCTION(Mertens, function19);
+ ADD_CALLBACK_FUNCTION(Mertens, function20);
+ ADD_CALLBACK_FUNCTION(Mertens, function21);
+ ADD_CALLBACK_FUNCTION(Mertens, function22);
+ ADD_CALLBACK_FUNCTION(Mertens, function23);
+ ADD_CALLBACK_FUNCTION(Mertens, function24);
+ ADD_CALLBACK_FUNCTION(Mertens, function25);
+ ADD_CALLBACK_FUNCTION(Mertens, function26);
+ ADD_CALLBACK_FUNCTION(Mertens, tylerCompartment);
+ ADD_CALLBACK_FUNCTION(Mertens, function28);
+ ADD_CALLBACK_FUNCTION(Mertens, function29);
+ ADD_CALLBACK_FUNCTION(Mertens, function30);
+ ADD_CALLBACK_FUNCTION(Mertens, function31);
+ ADD_CALLBACK_FUNCTION(Mertens, function32);
+ ADD_CALLBACK_FUNCTION(Mertens, function33);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter1);
+ ADD_CALLBACK_FUNCTION(Mertens, function35);
+ ADD_CALLBACK_FUNCTION(Mertens, function36);
+ ADD_CALLBACK_FUNCTION(Mertens, function37);
+ ADD_CALLBACK_FUNCTION(Mertens, function38);
+ ADD_CALLBACK_FUNCTION(Mertens, function39);
+ ADD_CALLBACK_FUNCTION(Mertens, function40);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Mertens, function42);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter2);
+ ADD_CALLBACK_FUNCTION(Mertens, function44);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter3);
+ ADD_CALLBACK_FUNCTION(Mertens, function46);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter4);
+ ADD_CALLBACK_FUNCTION(Mertens, function48);
+ ADD_CALLBACK_FUNCTION(Mertens, function49);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter5);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Mertens, function52);
+ ADD_CALLBACK_FUNCTION(Mertens, function53);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Mertens, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Mertens, bloodJacket)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityMertens, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Mertens, enterExitCompartment, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Mertens, enterExitCompartment2, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kAction4:
+ getEntities()->exitCompartment(kEntityMertens, (ObjectIndex)params->param4);
+ CALLBACK_ACTION();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(5, Mertens, enterExitCompartment3, ObjectIndex, EntityPosition, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4);
+ getData()->entityPosition = (EntityPosition)params->param5;
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq);
+ getEntities()->enterCompartment(_entityIndex, (ObjectIndex)params->param4);
+ getData()->entityPosition = (EntityPosition)params->param5;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, (EntityPosition)params->param5) || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, (EntityPosition)params->param6)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject((ObjectIndex)params->param4);
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Mertens, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Mertens, playSound)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(8, Mertens, playSound16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1, SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Mertens, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition)
+
+#define LOADSCENE_FROM_POSITION() \
+ if (getData()->direction != kDirectionUp) { \
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); \
+ } else { \
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); \
+ }
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 2000))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemInvalid);
+ else
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh);
+
+ if (!getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000)
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields10(kEntityPlayer)) {
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if ((ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) && (!getEvent(kEventKronosConversation) && getProgress().jacket == kJacketGreen)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2) && getProgress().jacket == kJacketGreen && !getProgress().eventMetAugust) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaiting);
+ break;
+ }
+
+ if (ENTITY_PARAM(2, 4) && getState()->time < kTime2133000) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosConcertInvitation);
+ break;
+ }
+
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ params->param3 = 0;
+ if (getProgress().eventCorpseFound || getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD)) {
+ if (ENTITY_PARAM(0, 4) && getProgress().jacket == kJacketGreen && !getEvent(kEventMertensDontMakeBed) && !getProgress().eventCorpseThrown) {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventMertensDontMakeBed);
+ }
+ } else {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartment);
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->playSound(kEntityMertens, "CON1110B");
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityMertens);
+ break;
+
+ case kActionDefault:
+ if ((!getProgress().eventCorpseFound && !getEvent(kEventMertensAskTylerCompartment) && !getEvent(kEventMertensAskTylerCompartment))
+ || (ENTITY_PARAM(0, 4) && getProgress().jacket == kJacketGreen && !getEvent(kEventMertensDontMakeBed) && !getProgress().eventCorpseThrown))
+ params->param3 = 1;
+
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensKronosInvitation : kEventMertensKronosInvitationClosedWindows);
+ getProgress().eventMertensKronosInvitation = true;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (params->param1 != 3 || (params->param2 != kPosition_8200 && params->param2 != kPosition_9510)) {
+ LOADSCENE_FROM_POSITION();
+ break;
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityMertens, kPosition_2000, 500))
+ getData()->entityPosition = kPosition_2500;
+
+ getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventMertensAugustWaiting);
+ getProgress().eventMertensAugustWaiting = true;
+
+ ENTITY_PARAM(1, 2) = 0;
+
+ if (params->param1 == 3 && params->param2 == kPosition_8200) {
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityMertens, kPosition_2000, 500))
+ getData()->entityPosition = kPosition_2500;
+
+ getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensKronosConcertInvitation);
+ ENTITY_PARAM(2, 4) = 0;
+
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 5:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensAskTylerCompartmentD : kEventMertensAskTylerCompartment);
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventMertensDontMakeBed);
+ LOADSCENE_FROM_POSITION();
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+ break;
+ }
+
+#undef LOADSCENE_FROM_POSITION
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Mertens, function11, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex)
+ EntityIndex entity = (EntityIndex)params->param1;
+
+ if (savepoint.action == kActionDefault)
+ return;
+
+ if (getSound()->isBuffered(kEntityMertens)) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ if (isNight()) {
+ if (Entities::isFemale(entity)) {
+ getSound()->playSound(kEntityMertens, rnd(2) ? "CON1112" : "CON1112A");
+ } else {
+ if (entity || getProgress().field_18 != 2) {
+ getSound()->playSound(kEntityMertens, "CON1112F");
+ } else {
+ switch (rnd(3)) {
+ default:
+ case 0:
+ getSound()->playSound(kEntityMertens, "CON1061");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "CON1110G");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, "CON1110H");
+ break;
+ }
+ }
+ }
+ } else {
+ if (Entities::isFemale(entity))
+ getSound()->playSound(kEntityMertens, rnd(2) ? "CON1112B" : "CON1112C");
+ else
+ getSound()->playSound(kEntityMertens, "CON1112G");
+ }
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ if (!params->param2 && !params->param3) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 75)
+ getData()->inventoryItem = kItemNone;
+ setCallback(5);
+ setup_function18();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 225)
+ getData()->inventoryItem = kItemNone;
+ setCallback(6);
+ setup_function18();
+ break;
+ UPDATE_PARAM_PROC_END
+
+ getData()->inventoryItem = (getProgress().chapter == kChapter1
+ && !ENTITY_PARAM(2, 1)
+ && !getProgress().eventCorpseFound
+ && !getEvent(kEventMertensAskTylerCompartment)
+ && !getEvent(kEventMertensAskTylerCompartmentD)) ? kItemMatchBox : kItemNone;
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartmentD);
+ break;
+
+ case kAction11:
+ params->param3++;
+ setCallback(11);
+ setup_bonsoir(savepoint.entity2);
+ break;
+
+ case kActionDefault:
+ if (params->param2)
+ params->param3 = 1;
+
+ if (!getSound()->isBuffered(kEntityMertens)) {
+
+ }
+
+ setCallback(3);
+ setup_function20();
+ break;
+
+ case kAction16:
+ params->param3--;
+
+ if (params->param2 && !params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function18();
+ }
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && ENTITY_PARAM(0, 7) && !getEvent(kEventKronosConversation)) {
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_function20();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityMertens, params->param1 ? "601I" : "601H");
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ case 6:
+ case 9:
+ case 10:
+ CALLBACK_ACTION();
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventMertensAskTylerCompartmentD);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventMertensKronosInvitation);
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ getScenes()->processScene();
+
+ if (!params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function18();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_1500);
+ } else {
+ setCallback(1);
+ setup_function11(15);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction202558662);
+
+ setCallback(2);
+ setup_function20();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction202558662);
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction125499160:
+ if (params->param1 == kEntityVerges)
+ ENTITY_PARAM(0, 8) = 0;
+
+ setCallback(5);
+ setup_function18();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(15, Mertens, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, params->param1 ? "CON1059A" : "CON1059");
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Xb", kObjectCompartment2);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction135664192);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Mertens, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ switch (rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityMertens, "AUG2095A");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "AUG2096A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, "AUG2094B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, "AUG2094C");
+ break;
+ }
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, params->param1 ? "AUG2097" : "AUG2098");
+
+ setCallback(4);
+ setup_enterExitCompartment("601Xc", kObjectCompartment3);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction69239528);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Mertens, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // FIXME: Check that we are using the correct parameter struct
+ if (ENTITY_PARAM(0, 6) || ((EntityData::EntityParametersIIII*)_data->getParameters(8, 1))->hasNonNullParameter()) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ // Mertens sits on his chair at the back of the train
+ if (!getInventory()->hasItem(kItemPassengerList) || ENTITY_PARAM(0, 2)) {
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ } else {
+ // Got the passenger list, Mertens is looking for it before sitting
+ ENTITY_PARAM(0, 2) = 1;
+ getSound()->playSound(kEntityMertens, "CON1058", SoundManager::kFlagInvalid, 75);
+ getEntities()->drawSequenceRight(kEntityMertens, "601D");
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 68)) {
+ getSound()->playSound(kEntityPlayer, "CON1110");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+ }
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityMertens);
+ ENTITY_PARAM(2, 1) = 1;
+ setCallback(2);
+ setup_function11(75);
+ break;
+
+ case 2:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ if (!ENTITY_PARAM(0, 3)
+ && !getInventory()->hasItem(kItemPassengerList)
+ && ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityMertens, kEntityVerges, kAction158617345);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ getSavePoints()->push(kEntityMertens, kEntityMertens, kActionDrawScene);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Mertens, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6)
+ || ENTITY_PARAM(1, 1)
+ || ENTITY_PARAM(1, 2)
+ || ENTITY_PARAM(1, 3)
+ || ENTITY_PARAM(1, 4)
+ || ENTITY_PARAM(1, 5)
+ || ENTITY_PARAM(1, 6)
+ || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(1, 8)) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!getInventory()->hasItem(kItemPassengerList) || ENTITY_PARAM(0, 2)) {
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ } else {
+ ENTITY_PARAM(0, 2) = 1;
+ getSound()->playSound(kEntityMertens, "CON1058", SoundManager::kFlagInvalid, 75);
+ getEntities()->drawSequenceRight(kEntityMertens, "601D");
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!ENTITY_PARAM(0, 3)
+ && !getInventory()->hasItem(kItemPassengerList)
+ && ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityMertens, kEntityVerges, kAction158617345);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Mertens, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 0;
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_bloodJacket("601C");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Mertens, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_bloodJacket("601C");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->time, 300)
+ getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityMertens));
+ UPDATE_PARAM_PROC_END
+
+ UPDATE_PARAM(CURRENT_PARAM(1, 5), getState()->time, 900);
+
+ // Update objects
+ getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1);
+ if (params->param5 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, (EntityIndex)params->param4, (ObjectLocation)params->param5, (CursorStyle)params->param6, (CursorStyle)params->param7);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param8, (ObjectLocation)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2), (CursorStyle)CURRENT_PARAM(1, 3));
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param3 = 1;
+ params->param4 = getObjects()->get((ObjectIndex)params->param1).entity;
+ params->param5 = getObjects()->get((ObjectIndex)params->param1).location;
+ params->param6 = getObjects()->get((ObjectIndex)params->param1).cursor;
+ params->param7 = getObjects()->get((ObjectIndex)params->param1).cursor2;
+
+ if (params->param2) {
+ params->param8 = getObjects()->get((ObjectIndex)params->param2).entity;
+ CURRENT_PARAM(1, 1) = getObjects()->get((ObjectIndex)params->param2).location;
+ CURRENT_PARAM(1, 2) = getObjects()->get((ObjectIndex)params->param2).cursor;
+ CURRENT_PARAM(1, 3) = getObjects()->get((ObjectIndex)params->param2).cursor2;
+
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ if (params->param5 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(params->param3 ? 3 : 4);
+ setup_playSound(params->param3 ? "Con1017" : "Con1017A");
+ break;
+
+ case 3:
+ case 4:
+ params->param3 = 0;
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Mertens, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Mh", kObjectCompartment8);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nh");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment8, true);
+
+ setCallback(3);
+ setup_function11(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Mh", kObjectCompartment8);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nh");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment8);
+ getSavePoints()->push(kEntityMertens, kEntityMahmud, kAction225563840);
+ break;
+
+ case 5:
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, "MAH1170I");
+
+ setCallback(6);
+ setup_enterExitCompartment("601Zd", kObjectCompartment4);
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, "MAH1172", SoundManager::kFlagInvalid, 225);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment4, kObject20);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("671Ad", kObjectCompartment4);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityMahmud, kAction123852928);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction102227384:
+ getEntities()->drawSequenceLeft(kEntityMertens, "671Dh");
+ break;
+
+ case kAction156567128:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment8, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Mertens, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Vd", kObjectCompartment4);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wd");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment4, true);
+
+ setCallback(3);
+ setup_function11(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Zd", kObjectCompartment4);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment4);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(5);
+ setup_function21(kObjectCompartment4, kObject20);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("671Ad", kObjectCompartment4);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Mertens, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment3("601Rc", kObjectCompartment3, kPosition_6470, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Mc", kObjectCompartment3);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction221617184);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nc");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment3, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(4);
+ setup_function21(kObjectCompartment3, kObjectKitchen);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("601Sc", kObjectCompartment3);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3, true);
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment3, kObjectKitchen);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(8);
+ setup_enterExitCompartment("601Uc", kObjectCompartment3);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction124697504);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction192849856);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Qc");
+ break;
+
+ case kAction102675536:
+ params->param1 = 1;
+ break;
+
+ case kAction156567128:
+ setCallback(6);
+ setup_enterExitCompartment("601Tc", kObjectCompartment3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Mertens, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment3("601Zb", kObjectCompartment2, kPosition_7500, kPositionNone);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Vb", kObjectCompartment2);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction221617184);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wb");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment2, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (getProgress().chapter == kChapter1 && ENTITY_PARAM(0, 4))
+ if (getProgress().field_14 != 29)
+ getProgress().field_14 = 3;
+
+ setCallback(4);
+ setup_function21(kObjectCompartment2, kObjectHandleInsideBathroom);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("671Ab", kObjectCompartment2);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2, true);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (getProgress().chapter == kChapter1 && ENTITY_PARAM(0, 4))
+ if (getProgress().field_14 != 29)
+ getProgress().field_14 = 3;
+
+ setCallback(7);
+ setup_function21(kObjectCompartment2, kObjectHandleInsideBathroom);
+ break;
+
+ case 7:
+ getSound()->playSound(kEntityMertens, "CON1024A");
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(8);
+ setup_enterExitCompartment("641Ub", kObjectCompartment2);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction124697504);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ params->param1 = 1;
+ break;
+
+ case kAction156567128:
+ setCallback(6);
+ setup_enterExitCompartment("641Tb", kObjectCompartment2);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().eventCorpseThrown
+ || !params->param1
+ || getProgress().chapter != kChapter1
+ || getProgress().jacket != kJacketGreen) {
+
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_playSound16("ZNU1001");
+ } else {
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound16("CON1062");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ } else if (getProgress().eventCorpseMovedFromFloor) {
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment1);
+ getEntities()->drawSequenceRight(kEntityMertens, "601Ra");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 16);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ } else {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment1);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment1, kObjectHandleBathroom);
+ break;
+
+ case 7:
+ if (getProgress().eventCorpseThrown || getProgress().chapter != kChapter1) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000)) {
+ if (!getEntities()->checkFields10(kEntityPlayer))
+ getSound()->playSound(kEntityMertens, "CON1061");
+ }
+
+ setCallback(9);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseBed);
+ }
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(27, Mertens, tylerCompartment, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 150)
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(10);
+ setup_playSound16("CON1018A");
+ break;
+ UPDATE_PARAM_PROC_END
+
+label_callback10:
+ if (!params->param3)
+ params->param3 = getState()->timeTicks + 300;
+
+ if (params->param3 >= getState()->timeTicks) {
+label_callback11:
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 375);
+
+ getSound()->playSound(kEntityPlayer, "LIB033");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(18);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(20);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(21);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(26);
+ setup_function26(false);
+ }
+
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(17);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ } else {
+ params->param3 = kTimeInvalid;
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(11);
+ setup_playSound16("CON1018B");
+ break;
+ }
+
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(13);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(16);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(14);
+ setup_function26(false);
+ }
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(12);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ switch (params->param1) {
+ default:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 1:
+ setCallback(23);
+ setup_playSound16("CON1018D");
+ break;
+
+ case 2:
+ setCallback(24);
+ setup_playSound16("CON1018E");
+ break;
+
+ case 3:
+ setCallback(25);
+ setup_playSound16("CON1025");
+ break;
+ }
+
+ } else {
+ setCallback(22);
+ setup_function26(true);
+ }
+ break;
+
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB012" : "LIB014");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(27);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(29);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(30);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(28);
+ setup_function26(false);
+ }
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(26);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(params->param1 ? 9 : 8);
+ setup_playSound16(params->param1 ? "CON1018" : "CON1060");
+ } else {
+ getSound()->playSound(kEntityMertens, "CON1019");
+
+ setCallback(1);
+ setup_enterExitCompartment("601Ma", kObjectCompartment1);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(4);
+ setup_enterExitCompartment("601Ra", kObjectCompartment1);
+ } else {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_enterExitCompartment("601Ra", kObjectCompartment1);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ break;
+
+ case 3:
+ case 12:
+ case 17:
+ case 26:
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (params->param1) {
+ setCallback(7);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ break;
+ }
+
+ if (getProgress().eventCorpseThrown || getProgress().chapter != kChapter1) {
+ setCallback(6);
+ setup_function21(kObjectCompartment1, kObjectHandleBathroom);
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseBed);
+ }
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 8:
+ case 9:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ break;
+
+ case 10:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ goto label_callback10;
+
+ case 11:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ goto label_callback11;
+
+ case 13:
+ case 18:
+ case 27:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 14:
+ case 19:
+ case 22:
+ case 28:
+ CALLBACK_ACTION();
+ break;
+
+ case 15:
+ case 20:
+ case 29:
+ getAction()->playAnimation(kEventMertensAugustWaitingCompartment);
+ getProgress().eventMertensAugustWaiting = true;
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 16:
+ case 21:
+ case 30:
+ getAction()->playAnimation(kEventMertensKronosInvitationCompartment);
+ getProgress().eventMertensKronosInvitation = true;
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 23:
+ getProgress().eventMertensAugustWaiting = true;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 24:
+ getProgress().eventMertensKronosInvitation = true;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 25:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(28, Mertens, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && params->param5) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param4 = 1;
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param5 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SS(29, Mertens, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param7 > 1 && params->param8) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param7++;
+ if (params->param7 == 1)
+ getSound()->playSound(kEntityMertens, (char *)&params->seq2);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param8 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (params->param1) {
+ default:
+ CALLBACK_ACTION();
+ return;
+
+ case 1:
+ params->param2 = kPosition_8200;
+
+ if (getProgress().field_14) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ getProgress().field_14 = 3;
+ break;
+
+ case 2:
+ params->param2 = kPosition_7500;
+ break;
+
+ case 3:
+ params->param2 = kPosition_6470;
+ break;
+ }
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, (EntityPosition)params->param2);
+ break;
+
+ case 2:
+ switch (params->param1) {
+ default:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 1:
+ if (getProgress().chapter == kChapter4)
+ getSavePoints()->push(kEntityMertens, kEntityTatiana, kAction238790488);
+
+ setCallback(3);
+ setup_tylerCompartment(kMertensAction3);
+ break;
+
+ case 2:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, getObjects()->get(kObjectCompartment2).location, kCursorNormal, kCursorNormal);
+ params->param3 = 1;
+ }
+
+ setCallback(4);
+ setup_enterExitCompartment("601Vb", kObjectCompartment2);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_6470)) {
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, getObjects()->get(kObjectCompartment3).location, kCursorNormal, kCursorNormal);
+ params->param3 = 1;
+ }
+
+ setCallback(6);
+ setup_enterExitCompartment("601Mc", kObjectCompartment3);
+ break;
+ }
+ break;
+
+ case 3:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wb");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment2, true);
+
+ setCallback(5);
+ setup_playSound("CON3020");
+ break;
+
+ case 5:
+ if (params->param3)
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, getObjects()->get(kObjectCompartment2).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2);
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nc");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment3, true);
+
+ setCallback(7);
+ setup_playSound("CON3020");
+ break;
+
+ case 7:
+ if (params->param3)
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, getObjects()->get(kObjectCompartment3).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3);
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function17();
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(31, Mertens, function31, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setCallback(3);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_bloodJacket("601G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityMertens)) {
+ getEntities()->drawSequenceLeft(kEntityMertens, "601J");
+ } else {
+ setCallback(2);
+ setup_function17();
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Mertens, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9510);
+ break;
+
+ case 2:
+ if (getData()->entityPosition >= kPosition_9460) {
+ getEntities()->clearSequences(kEntityMertens);
+ setCallback(3);
+ setup_function11(900);
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function17();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Mertens, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 8) || ENTITY_PARAM(0, 6)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 4) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(1, 8) = 1;
+
+ setCallback(ENTITY_PARAM(0, 8) ? 1 : 3);
+ setup_updateEntity(kCarGreenSleeping, ENTITY_PARAM(0, 8) ? kPosition_1500 : kPosition_540);
+ } else {
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(2, 1) = 1;
+
+ setCallback(2);
+ setup_function14(kEntityVerges);
+ break;
+
+ case 2:
+ ENTITY_PARAM(1, 8) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(4);
+ setup_function11(75);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(5);
+ setup_function16(true);
+ break;
+ }
+ // Fallback to next case
+
+ case 5:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(6);
+ setup_function16(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 6:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(7);
+ setup_function15(true);
+ break;
+ }
+ // Fallback to next case
+
+ case 7:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(8);
+ setup_function15(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 8:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(9);
+ setup_function35();
+ break;
+ }
+ // Fallback to next case
+
+ case 9:
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(10);
+ setup_function36();
+ break;
+ }
+ // Fallback to next case
+
+ case 10:
+ if (ENTITY_PARAM(1, 3)) {
+ setCallback(11);
+ setup_function40();
+ break;
+ }
+ // Fallback to next case
+
+ case 11:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(12);
+ setup_function28("CON1200");
+ break;
+ }
+
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(13);
+ setup_function37();
+ break;
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case 12:
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction168254872);
+ ENTITY_PARAM(1, 1) = 0;
+
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(13);
+ setup_function37();
+ break;
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case 13:
+ ENTITY_PARAM(2, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Mertens, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMertens, kAction171394341, 7);
+ getSavePoints()->addData(kEntityMertens, kAction169633856, 9);
+ getSavePoints()->addData(kEntityMertens, kAction238732837, 10);
+ getSavePoints()->addData(kEntityMertens, kAction269624833, 12);
+ getSavePoints()->addData(kEntityMertens, kAction302614416, 11);
+ getSavePoints()->addData(kEntityMertens, kAction190082817, 8);
+ getSavePoints()->addData(kEntityMertens, kAction269436673, 13);
+ getSavePoints()->addData(kEntityMertens, kAction303343617, 14);
+ getSavePoints()->addData(kEntityMertens, kAction224122407, 17);
+ getSavePoints()->addData(kEntityMertens, kAction201431954, 18);
+ getSavePoints()->addData(kEntityMertens, kAction188635520, 19);
+ getSavePoints()->addData(kEntityMertens, kAction204379649, 4);
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Mertens, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ break;
+ } else {
+ getProgress().field_14 = 3;
+
+ setCallback(1);
+ setup_function19();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ if (!ENTITY_PARAM(1, 2) || getProgress().eventMetAugust) {
+ ENTITY_PARAM(1, 2) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ } else {
+ setCallback(5);
+ setup_tylerCompartment(kMertensAction1);
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ if (getProgress().eventMertensAugustWaiting)
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(7);
+ setup_function17();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Mertens, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ } else {
+ getProgress().field_14 = 3;
+
+ setCallback(1);
+ setup_function19();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ if (ENTITY_PARAM(0, 6)) {
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ } else {
+ setCallback(7);
+ setup_tylerCompartment(kMertensAction2);
+ }
+ } else {
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(7);
+ setup_tylerCompartment(kMertensAction2);
+ } else {
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 7:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ if (!getProgress().eventMertensKronosInvitation)
+ ENTITY_PARAM(0, 7) = 1;
+
+ ENTITY_PARAM(0, 6) = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function17();
+ break;
+
+ case 6:
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Mertens, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 >= 2 && params->param2) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ ++params->param6;
+
+ if (params->param6 == 1)
+ getSound()->playSound(kEntityMertens, getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 2000) ? "CON1152" : "CON1151");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param2 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, "CON1150");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Mertens, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!ENTITY_PARAM(0, 4)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!ENTITY_PARAM(0, 4)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(2);
+ setup_tylerCompartment(kMertensActionNone);
+ break;
+
+ case 2:
+ ENTITY_PARAM(0, 4) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Mertens, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 4) = 1;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function22();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function24();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function25();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function38();
+ break;
+
+ case 8:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function17();
+ break;
+
+ case 10:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Mertens, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 3) = 0;
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_1500);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function17();
+ break;
+
+ case 5:
+ ENTITY_PARAM(0, 6) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Mertens, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17();
+ break;
+
+ case 2:
+ setup_function42();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Mertens, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(0, 1) = 1;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 1) = 0; // BUG: is set twice. Maybe a bug?
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+
+ params->param1 = 1;
+ params->param2 = 1;
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+ }
+
+ if (ENTITY_PARAM(2, 1) || getProgress().eventCorpseFound || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAskTylerCompartment))
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (!params->param2) {
+ TIME_CHECK_SAVEPOINT(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072);
+
+ if (params->param4 != kTimeInvalid && getState()->time > kTimeCityChalons) {
+
+ if (getState()->time <= kTime1188000) {
+ if ((!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getSound()->isBuffered("REB1205")
+ || !getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)
+ || !params->param4) {
+ params->param4 = (uint)getState()->time;
+ }
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ ENTITY_PARAM(0, 4) = kTimeInvalid;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(8);
+ setup_function29("CON1210", "CON1210A");
+ break;
+ }
+ }
+
+label_callback_8:
+ if (getState()->time > kTime1215000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM_PROC(params->param5, getState()->time, 2700)
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+ ENTITY_PARAM(0, 1) = 1;
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(9);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+ if (getProgress().field_14 == 29)
+ goto label_callback_13;
+
+label_callback_9:
+ if (ENTITY_PARAM(1, 6)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_10:
+ if (ENTITY_PARAM(1, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(11);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_11:
+ if (ENTITY_PARAM(1, 5)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(12);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_12:
+ if (ENTITY_PARAM(1, 4)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(13);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_13:
+ if (ENTITY_PARAM(1, 2)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(14);
+ setup_function35();
+ break;
+ }
+
+label_callback_14:
+ if (ENTITY_PARAM(0, 6)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(15);
+ setup_function36();
+ break;
+ }
+
+label_callback_15:
+ if (ENTITY_PARAM(1, 3)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(16);
+ setup_function40();
+ break;
+ }
+
+label_callback_16:
+ if (ENTITY_PARAM(1, 1)) {
+ ENTITY_PARAM(1, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+ setCallback(17);
+ setup_function28("CON1200");
+ break;
+ }
+
+label_callback_17:
+ if (ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(2, 2) = 0;
+ getData()->inventoryItem = kItemNone;
+ setCallback(18);
+ setup_function37();
+ break;
+ }
+
+label_callback_18:
+ if (!params->param1 && ENTITY_PARAM(0, 5)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(19);
+ setup_function39();
+ break;
+ }
+
+label_callback_19:
+ if (ENTITY_PARAM(0, 1) && !getSound()->isBuffered(kEntityMertens)) {
+ if (getProgress().field_18 != 4)
+ getSound()->playSound(kEntityMertens, "CON1505");
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(21);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartmentD);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(20);
+ setup_function13((bool)savepoint.param.intValue, (bool)savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && ENTITY_PARAM(0, 7) && !getEvent(kEventKronosConversation)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && !getProgress().eventMertensKronosInvitation && !getEvent(kEventMertensLastCar) && !getEvent(kEventMertensLastCarOriginalJacket)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensLastCar);
+ break;
+ }
+
+label_callback_2_4:
+ if ((getEntities()->isPlayerPosition(kCarGreenSleeping, 1) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(getEntities()->isPlayerPosition(kCarGreenSleeping, 1) ? 5 : 6);
+ setup_function13(getEntities()->isPlayerPosition(kCarGreenSleeping, 1), false);
+ break;
+ }
+
+label_callback_5_6:
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (getProgress().jacket == kJacketOriginal || ENTITY_PARAM(0, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_function32();
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventMertensKronosInvitation);
+ getProgress().eventMertensKronosInvitation = true;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ goto label_callback_2_4;
+
+ case 3:
+ getAction()->playAnimation(getProgress().jacket == kJacketOriginal ? kEventMertensLastCarOriginalJacket : kEventMertensLastCar);
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 6);
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ case 6:
+ goto label_callback_5_6;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+
+ case 15:
+ goto label_callback_15;
+
+ case 16:
+ goto label_callback_16;
+
+ case 17:
+ goto label_callback_17;
+
+ case 18:
+ goto label_callback_18;
+
+ case 19:
+ params->param1 = 1;
+ goto label_callback_19;
+
+ case 21:
+ getAction()->playAnimation(kEventMertensAskTylerCompartmentD);
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getInventory()->get(kItem7)->location = kObjectLocationNone;
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+
+ setCallback(22);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 22:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(23);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(24);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Mertens, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function44();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Mertens, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(5);
+ setup_function13((bool)savepoint.param.intValue, (bool)savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(6);
+ setup_function13(true, false);
+
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(7);
+ setup_function13(false, false);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(8);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(10);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Mertens, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 3) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function46();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Mertens, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(5);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(2, 4)
+ && (getEvent(kEventKronosVisit) || getState()->time > kTime2052000)
+ && getState()->time < kTime2133000
+ && getEntities()->isPlayerInCar(kCarGreenSleeping)) {
+ setCallback(6);
+ setup_function32();
+ break;
+ }
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 7, setup_function28, "CON3012");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2117700, params->param2, 8, setup_function32);
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2124000, params->param3, 9, setup_function28, "CON2010");
+
+label_callback_9:
+ TIME_CHECK_CALLBACK(kTime2146500, params->param4, 10, setup_function32);
+
+label_callback_10:
+ TIME_CHECK_CALLBACK(kTime2169000, params->param5, 11, setup_function32);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(12);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2 != kEntityPlayer);
+ }
+ break;
+
+ case kActionDefault:
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1)) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(13);
+ setup_function13(true, false);
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(14);
+ setup_function13(false, false);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(16);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(15);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(17);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Mertens, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function48();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Mertens, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ params->param1 = 1;
+
+ getObjects()->updateLocation2(kObjectCompartment2, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment3, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment4, kObjectLocation1);
+
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+
+ ENTITY_PARAM(2, 3) = 0;
+ }
+
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_4:
+ if (!params->param1) {
+ TIME_CHECK_CALLBACK(kTime2403000, params->param2, 5, setup_function49);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2430000, params->param3, 6, setup_function32);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK(kTime2439000, params->param4, 7, setup_function32);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2448000, params->param5, 8, setup_function32);
+ }
+
+label_callback_8:
+ if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM(params->param6, getState()->time, 2700);
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+
+ ENTITY_PARAM(0, 1) = 1;
+ params->param6 = 0;
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2 != kEntityPlayer);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(10);
+ setup_function13(true, false);
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(11);
+ setup_function13(false, false);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(13);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(12);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(14);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Mertens, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_tylerCompartment(kMertensActionNone);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function33();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function25();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function33();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function24();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function33();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function23();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function17();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Mertens, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Mertens, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function52();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Mertens, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param2)
+ params->param2 = (uint)getState()->time;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Mme5010");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ params->param1 = (uint)(getState()->time + 4500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("671Ad", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityMmeBoutarel, kAction155604840);
+ setup_function53();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Mertens, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(3);
+ setup_playSound(getSound()->justCheckingCath());
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 4 : 5);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param1) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601ZD", kObjectCompartment4);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMertens);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ // Fallback to next case
+
+ case 3:
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 4:
+ case 5:
+ params->param3++;
+
+ if (params->param3 == 1) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("Con5002");
+
+ } else if (params->param3 == 2) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_playSound("Con5002A");
+ }
+ break;
+
+ case 6:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 7:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(54, Mertens)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mertens.h b/engines/lastexpress/entities/mertens.h
new file mode 100644
index 0000000000..ccce17795c
--- /dev/null
+++ b/engines/lastexpress/entities/mertens.h
@@ -0,0 +1,220 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_MERTENS_H
+#define LASTEXPRESS_MERTENS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Mertens : public Entity {
+private:
+ // The type of action when entering Tyler compartment
+ enum MertensActionType {
+ kMertensActionNone = 0,
+ kMertensAction1 = 1,
+ kMertensAction2 = 2,
+ kMertensAction3 = 3
+ };
+
+public:
+ Mertens(LastExpressEngine *engine);
+ ~Mertens() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handle meeting Coudert with the blooded jacket
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(bloodJacket, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param entityPosition1 The entity position
+ * @param entityPosition1 The entity position to check
+ *
+ * @note We are not using the shared function due to too many differences
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment3, const char *sequence, ObjectIndex compartment, EntityPosition entityPosition1, EntityPosition entityPosition2)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function11, uint32 time)
+
+ /**
+ * Says "Bonsoir" to another character
+ *
+ * @param entity The entity
+ */
+ DECLARE_FUNCTION_1(bonsoir, EntityIndex entity)
+ DECLARE_FUNCTION_2(function13, bool, bool)
+ DECLARE_FUNCTION_1(function14, EntityIndex entity)
+ DECLARE_FUNCTION_1(function15, bool)
+ DECLARE_FUNCTION_1(function16, bool)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * ???
+ *
+ * @param object1 First object index
+ * @param object2 Second object index
+ */
+ DECLARE_FUNCTION_2(function21, ObjectIndex object1, ObjectIndex object2)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION_1(function26, bool)
+ DECLARE_FUNCTION_1(tylerCompartment, MertensActionType action)
+ DECLARE_FUNCTION_1(function28, const char *soundName)
+ DECLARE_FUNCTION_2(function29, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION_1(function30, MertensActionType action)
+ DECLARE_FUNCTION_1(function31, MertensActionType action)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function42)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function44)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function46)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MERTENS_H
diff --git a/engines/lastexpress/entities/milos.cpp b/engines/lastexpress/entities/milos.cpp
new file mode 100644
index 0000000000..50b0c04f45
--- /dev/null
+++ b/engines/lastexpress/entities/milos.cpp
@@ -0,0 +1,1805 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/milos.h"
+
+#include "lastexpress/entities/vesna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Milos::Milos(LastExpressEngine *engine) : Entity(engine, kEntityMilos) {
+ ADD_CALLBACK_FUNCTION(Milos, reset);
+ ADD_CALLBACK_FUNCTION(Milos, draw);
+ ADD_CALLBACK_FUNCTION(Milos, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Milos, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Milos, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Milos, playSound);
+ ADD_CALLBACK_FUNCTION(Milos, playSound16);
+ ADD_CALLBACK_FUNCTION(Milos, savegame);
+ ADD_CALLBACK_FUNCTION(Milos, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Milos, enterCompartmentDialog);
+ ADD_CALLBACK_FUNCTION(Milos, function11);
+ ADD_CALLBACK_FUNCTION(Milos, chapter1);
+ ADD_CALLBACK_FUNCTION(Milos, function13);
+ ADD_CALLBACK_FUNCTION(Milos, function14);
+ ADD_CALLBACK_FUNCTION(Milos, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function16);
+ ADD_CALLBACK_FUNCTION(Milos, function17);
+ ADD_CALLBACK_FUNCTION(Milos, function18);
+ ADD_CALLBACK_FUNCTION(Milos, chapter2);
+ ADD_CALLBACK_FUNCTION(Milos, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function21);
+ ADD_CALLBACK_FUNCTION(Milos, chapter3);
+ ADD_CALLBACK_FUNCTION(Milos, function23);
+ ADD_CALLBACK_FUNCTION(Milos, function24);
+ ADD_CALLBACK_FUNCTION(Milos, function25);
+ ADD_CALLBACK_FUNCTION(Milos, function26);
+ ADD_CALLBACK_FUNCTION(Milos, function27);
+ ADD_CALLBACK_FUNCTION(Milos, chapter4);
+ ADD_CALLBACK_FUNCTION(Milos, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function30);
+ ADD_CALLBACK_FUNCTION(Milos, function31);
+ ADD_CALLBACK_FUNCTION(Milos, function32);
+ ADD_CALLBACK_FUNCTION(Milos, chapter5);
+ ADD_CALLBACK_FUNCTION(Milos, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function35);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Milos, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Milos, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Milos, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Milos, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Milos, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Milos, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Milos, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Milos, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(9, Milos, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Milos, enterCompartmentDialog, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ if (getEvent(kEventMilosTylerCompartmentDefeat)) {
+ // Robert saying: "Milos"
+ switch(rnd(3)) {
+ default:
+ case 0:
+ getSound()->playSound(kEntityPlayer, "CAT1014");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "CAT1014A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityPlayer, "CAT1014B");
+ break;
+ }
+ } else {
+ getSound()->excuseMeCath();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param5 && params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param8, getState()->timeTicks, 75)
+ params->param2 = 0;
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal);
+ UPDATE_PARAM_PROC_END
+ }
+
+ params->param8 = 0;
+
+ if (getProgress().chapter != kChapter1 || params->param5)
+ break;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 4500)
+ params->param6 = 0;
+ CURRENT_PARAM(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getProgress().field_CC) {
+
+ if (ENTITY_PARAM(0, 3) && !getProgress().field_14 && !params->param6) {
+ getProgress().field_14 = 14;
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction190412928);
+
+ setCallback(1);
+ setup_enterExitCompartment("609Cg", kObjectCompartmentG);
+ }
+ break;
+ }
+
+ if (!params->param4)
+ params->param4 = (uint)getState()->time + 18000;
+
+ if (CURRENT_PARAM(1, 2) != kTimeInvalid) {
+ if (params->param4 >= getState()->time) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityMilos, 2000) || !CURRENT_PARAM(1, 2))
+ CURRENT_PARAM(1, 2) = (uint)getState()->time + 150;
+
+ if (CURRENT_PARAM(1, 2) >= getState()->time)
+ break;
+ }
+
+ CURRENT_PARAM(1, 2) = kTimeInvalid;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityMilos, 2000))
+ getProgress().field_98 = 1;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("MIL1012");
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(10);
+ setup_playSound((rnd(2) ? "CAT1504" : getSound()->wrongDoorCath()));
+ } else {
+ setCallback(11);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 7 : 8);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param3 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch(getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(2);
+ setup_enterCompartmentDialog(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function14();
+ break;
+
+ case 3:
+ if (getProgress().field_14 == 14)
+ getProgress().field_14 = 0;
+
+ params->param6 = 1;
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 7:
+ case 8:
+ setCallback(9);
+ // Milos asking: "Yeah? Who is it?"
+ setup_playSound("MIL1117A");
+ break;
+
+ case 9:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 10:
+ case 11:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 12:
+ getEntities()->drawSequenceLeft(kEntityMilos, "611Cg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction88652208);
+ break;
+
+ case 13:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param5 = 0;
+ break;
+
+ }
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(12);
+ setup_enterExitCompartment("611Bg", kObjectCompartmentG);
+ break;
+
+ case kAction123852928:
+ params->param1 = 13;
+ setup_enterExitCompartment("611Dg", kObjectCompartmentG);
+ break;
+
+ case kAction221683008:
+ params->param5 = 1;
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Milos, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject46, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getSavePoints()->addData(kEntityMilos, kAction157691176, 0);
+ getSavePoints()->addData(kEntityMilos, kAction208228224, 2);
+ getSavePoints()->addData(kEntityMilos, kAction259125998, 3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Milos, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityMilos, kEntityTables2, kActionDrawTablesWithChairs, "009E");
+ getEntities()->clearSequences(kEntityVesna);
+ getEntities()->clearSequences(kEntityIvo);
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntitySalko, "009D5");
+ getEntities()->drawSequenceRight(kEntityTables2, "009D4");
+ getEntities()->drawSequenceRight(kEntityIvo, "009D3");
+ getEntities()->drawSequenceRight(kEntityVesna, "009D2");
+ getEntities()->drawSequenceRight(kEntityMilos, "009D1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Milos, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29 || getProgress().field_14 == 3) {
+ if (params->param2) {
+ setCallback(1);
+ setup_enterExitCompartment("609Ca", kObjectCompartment1);
+ } else {
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (params->param1) {
+
+ // TODO replace with UPDATE_PARAM_PROC (without the kTimeInvalid part)
+ if (!CURRENT_PARAM(1, 1))
+ CURRENT_PARAM(1, 1) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAM(1, 1) < getState()->timeTicks) {
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ ++params->param5;
+ switch (params->param5) {
+ default:
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, params->param3 < 1 ? kCursorTalk : kCursorNormal, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ break;
+
+ case 1:
+ setCallback(6);
+ setup_playSound("LIB013");
+ break;
+
+ case 2:
+ setCallback(8);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ setCallback(10);
+ setup_playSound("LIB012");
+ break;
+
+ case 4:
+ ++params->param7;
+
+ if (params->param7 < 3) {
+ params->param5 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, params->param3 < 1 ? kCursorTalk : kCursorNormal, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ break;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ params->param6 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMilosTylerCompartmentBedVisit : kEventMilosTylerCompartmentVisit;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosTylerCompartmentVisit);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ }
+ }
+ }
+ break;
+ }
+
+ // TODO replace with UPDATE_PARAM_PROC (without the kTimeInvalid part)
+ if (!CURRENT_PARAM(1, 3))
+ CURRENT_PARAM(1, 3) = getState()->timeTicks + 75;
+
+ if (CURRENT_PARAM(1, 3) < getState()->timeTicks) {
+
+ if (!params->param4) {
+ setCallback(12);
+ setup_playSound("MIL1030C");
+ break;
+ }
+
+label_callback_12:
+ UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->timeTicks, 75);
+
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true);
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(13);
+ setup_enterExitCompartment("609Ba", kObjectCompartment1);
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(14);
+ setup_enterExitCompartment2("609Ba", kObjectCompartment1);
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param2) {
+ getObjects()->update(kObjectCompartment1, kEntityMilos, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(20);
+ setup_playSound("LIB012");
+ } else if (!params->param3) {
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(22);
+ setup_playSound16("MIL1032");
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ if (params->param2) {
+ getEntityData(kEntityPlayer)->location = kLocationInsideCompartment;
+ params->param6 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMilosTylerCompartmentBed : kEventMilosTylerCompartment;
+ } else {
+ params->param6 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMilosTylerCompartmentBedVisit : kEventMilosTylerCompartmentVisit;
+ }
+
+ setCallback(17);
+ setup_savegame(kSavegameTypeEvent, kEventMilosTylerCompartmentVisit);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(16);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+
+ params->param1 = 1;
+ } else {
+ getEntities()->drawSequenceLeft(kEntityMilos, "609Aa");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartment1, true);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventMilosCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseMovedFromFloor ? kSceneGameOverBloodJacket : kSceneGameOverPolice1, true);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation((EventIndex)params->param6);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 4:
+ case 18:
+ params->param8 = getFight()->setup(kFightMilos);
+ if (params->param8) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param8 == Fight::kFightEndLost);
+ } else {
+ getState()->time = (TimeValue)(getState()->time + 1800);
+ getProgress().field_CC = 1;
+
+ setCallback(getCallback() + 1);
+ setup_savegame(kSavegameTypeEvent, kEventMilosTylerCompartmentDefeat);
+ }
+ break;
+
+ case 5:
+ case 19:
+ getAction()->playAnimation(kEventMilosTylerCompartmentDefeat);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSound16("MIL1031C");
+ break;
+
+ case 7:
+ case 9:
+ case 11:
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, params->param3 < 1 ? kCursorTalk : kCursorNormal, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_playSound16("MIL1031A");
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_playSound16("MIL1031B");
+ break;
+
+ case 12:
+ params->param4 = 1;
+ goto label_callback_12;
+
+ case 13:
+ params->param2 = 1;
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityMilos, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 14:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ break;
+
+ case 15:
+ getAction()->playAnimation(kEventMilosCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 16:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getAction()->playAnimation(kEventMilosCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseMovedFromFloor ? kSceneGameOverBloodJacket : kSceneGameOverPolice1, true);
+ break;
+
+ case 17:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getAction()->playAnimation((EventIndex)params->param6);
+
+ setCallback(18);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 20:
+ setCallback(21);
+ setup_playSound("MIL1117A");
+ break;
+
+ case 21:
+ getObjects()->update(kObjectCompartment1, kEntityMilos, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 22:
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Milos, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560);
+
+ if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setup_function16();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 61) && !params->param1) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 45)
+ setCallback(1);
+ setup_draw("009C");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 70) && !params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 45);
+
+ setCallback(2);
+ setup_draw("009C");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityMilos, kEntityTables2, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ params->param1 = 1;
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Milos, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityVesna, 750)
+ || getEntities()->checkDistanceFromPosition(kEntityVesna, kPosition_3050, 500)) {
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction123668192);
+
+ setCallback(5);
+ setup_enterExitCompartment("611Ag", kObjectCompartmentG);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMilos, kEntityServers1, kAction269485588);
+ getSavePoints()->push(kEntityMilos, kEntityIvo, kAction125242096);
+ getEntities()->drawSequenceRight(kEntityMilos, "807DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityMilos);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+
+ case 3:
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityVesna, 750)
+ || getEntities()->checkDistanceFromPosition(kEntityVesna, kPosition_3050, 500)) {
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction123668192);
+
+ setCallback(4);
+ setup_enterExitCompartment("611Ag", kObjectCompartmentG);
+ } else {
+ params->param1 = 1;
+
+ getEntities()->drawSequenceLeft(kEntityMilos, "609Dg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ }
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+
+ setup_function17();
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+
+ setup_function17();
+ break;
+ }
+ break;
+
+ case kAction135024800:
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction204832737);
+
+ setCallback(3);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Milos, function17)
+ if (savepoint.action == kActionDefault) {
+ setCallback(1);
+ setup_function11(kTimeBedTime);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Milos, function18)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Milos, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject46, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Milos, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction137165825);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerInCar(kCarRedSleeping) && !getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(1);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Milos, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ params->param1 = 1;
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB012");
+ break;
+
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitAugust);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!getEvent(kEventMilosCompartmentVisitAugust)
+ && !getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)
+ && params->param1)
+ setup_chapter2Handler();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Mil1118");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventMilosCompartmentVisitAugust);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 5);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction135024800);
+
+ setCallback(4);
+ setup_function11(kTimeEnd);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Milos, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->events[kEventMilosCompartmentVisitAugust])
+ setup_function24();
+ else
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Milos, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2106000 && !params->param1) {
+ params->param1 = 1;
+
+ setCallback(1);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction137165825);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerInCar(kCarRedSleeping)
+ && !getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(3);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ case 4:
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+
+ setup_function24();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Milos, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4)
+ params->param4 = (uint)getState()->time + 4500;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ params->param3 = 1;
+ }
+
+ if (ENTITY_PARAM(0, 1)) {
+ setCallback(1);
+ setup_enterExitCompartment("609Cg", kObjectCompartmentG);
+ break;
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(9);
+ setup_playSound(rnd(2) ? "CAT1504" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(10);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(6);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getState()->time >= kTime2106000) {
+ setCallback(12);
+ setup_playSound("LIB013");
+ } else {
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitAugust);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (getEvent(kEventMilosCompartmentVisitAugust)
+ || getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)
+ || !params->param3
+ || getState()->time >= kTime2106000) {
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+ }
+
+ setup_function23();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction203663744);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_function26(kTime2223000);
+ break;
+
+ case 2:
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorridorThanksD);
+ } else {
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation((getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) ? kEventMilosCorridorThanksD : kEventMilosCorridorThanks);
+
+ if (getData()->car == kCarRedSleeping && getEntities()->checkDistanceFromPosition(kEntityMilos, kPosition_3050, 500))
+ getData()->entityPosition = kPosition_3550;
+
+ getEntities()->updateEntity(kEntityMilos, kCarRedSleeping, kPosition_3050);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionDown ? 1 : -1))), getData()->direction != kDirectionDown);
+
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("609BG", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ ENTITY_PARAM(0, 1) = 0;
+
+ setup_function25();
+ break;
+
+ case 6:
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getState()->time >= kTime2106000) {
+ setCallback(8);
+ setup_playSound("Mil1117A");
+ } else {
+ setCallback(7);
+ setup_playSound("Mil1118");
+ }
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 8:
+ case 13:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 9:
+ case 10:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 11:
+ getAction()->playAnimation(kEventMilosCompartmentVisitAugust);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 5);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction135024800);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_playSound("MIL1117A");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Milos, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEvent(kEventMilosCompartmentVisitTyler) && !getProgress().field_54 && !ENTITY_PARAM(0, 4)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 13500)
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction155913424);
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, params->param1 ? kObjectLocation3 : kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1505" : "CAT1505A");
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (!getEvent(kEventMilosCompartmentVisitTyler) && !getProgress().field_54 && !ENTITY_PARAM(0, 4))
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction155913424);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (getEntities()->isInsideCompartment(kEntityVesna, kCarRedSleeping, kPosition_3050)) {
+ setCallback(3);
+ setup_playSound("VES1015A");
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitTyler) || ENTITY_PARAM(0, 4)) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_chapter3Handler);
+
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitTyler);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 1;
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMilosCompartmentVisitTyler);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 5);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 5:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_function27(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_function27(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 2) = 0;
+
+ setCallback(1);
+ setup_function27(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function27(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ if (getData()->car == kCarRedSleeping || getData()->car == kCarGreenSleeping) {
+ ENTITY_PARAM(0, 2) = 1;
+
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Milos, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Milos, chapter4Handler)
+#define TIME_CHECK_PLAYSOUND_MILOS(timeValue, parameter, sound) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getSound()->playSound(kEntityMilos, sound); \
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityPlayer, 2000)) \
+ getProgress().field_94 = 1; \
+ break; \
+ }
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1)
+ break;
+
+ if (params->param2) {
+ setup_function30();
+ break;
+ }
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2356200, params->param3, "Mil4013");
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2360700, params->param4, "Mil4014");
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2370600, params->param5, "Mil4015");
+
+ TIME_CHECK_SAVEPOINT(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityMilos, "611Cg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction88652208);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_3050;
+
+ getEntities()->clearSequences(kEntityMilos);
+
+ params->param1 = 0;
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ setCallback(1);
+ setup_enterExitCompartment("611Bg", kObjectCompartmentG);
+ break;
+
+ case kAction123852928:
+ setCallback(2);
+ setup_enterExitCompartment("611Dg", kObjectCompartmentG);
+ break;
+
+ case kAction135600432:
+ params->param2 = 1;
+ break;
+
+ case kAction221683008:
+ if (getSound()->isBuffered(kEntityMilos))
+ getSound()->processEntry(kEntityMilos);
+
+ params->param1 = 1;
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction123199584);
+ break;
+ }
+
+#undef TIME_CHECK_PLAYSOUND_MILOS
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Milos, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function11(kTime2410200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMilos, kEntityIvo, kAction55996766);
+
+ setCallback(2);
+ setup_function11(kTime2412000);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMilos, kEntitySalko, kAction55996766);
+
+ setCallback(3);
+ setup_function11(kTime2415600);
+ break;
+
+ case 3:
+ setup_function31();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Milos, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_enterExitCompartment("609CG", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterCompartmentDialog(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Milos, function32)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarCoalTender;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Milos, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarCoalTender;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Milos, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ if (!getProgress().isNightTime) {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventTrainStopped);
+ break;
+ }
+
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverTrainStopped2, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(isNight() ? kEventLocomotiveMilosShovelingNight : kEventLocomotiveMilosShovelingDay);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ if (getSound()->isBuffered("ARRIVE"))
+ getSound()->removeFromQueue("ARRIVE");
+
+ getSound()->processEntries();
+ getAction()->playAnimation(isNight() ? kEventLocomotiveMilosNight : kEventLocomotiveMilosDay);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityMilos);
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 1);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventLocomotiveAnnaStopsTrain);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventLocomotiveMilosDay, kSceneGameOverTrainStopped, true);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventLocomotiveRestartTrain);
+ getAction()->playAnimation(kEventLocomotiveOldBridge);
+ getSound()->resetState();
+ getState()->time = kTime2983500;
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 2, 1);
+ getSavePoints()->push(kEntityMilos, kEntityAbbot, kAction135600432);
+
+ setup_function35();
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventTrainStopped);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverTrainStopped, true);
+ break;
+ }
+ break;
+
+ case kAction168646401:
+ if (!getEvent(kEventLocomotiveMilosShovelingDay) && !getEvent(kEventLocomotiveMilosShovelingNight)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveMilosShovelingDay);
+ break;
+ }
+
+ if (!getEvent(kEventLocomotiveMilosDay) && !getEvent(kEventLocomotiveMilosNight)) {
+ if (getProgress().isNightTime && getState()->time < kTimeTrainStopped2)
+ getState()->time = kTimeTrainStopped2;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveMilosDay);
+ }
+ break;
+
+ case kAction169773228:
+ if (!getProgress().isNightTime) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAnnaStopsTrain);
+ }
+
+ getSound()->processEntry(kEntityMilos);
+ if (getState()->time < kTimeTrainStopped2)
+ getState()->time = kTimeTrainStopped2;
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveRestartTrain);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Milos, function35)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityMilos);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/milos.h b/engines/lastexpress/entities/milos.h
new file mode 100644
index 0000000000..6d44d1c4d9
--- /dev/null
+++ b/engines/lastexpress/entities/milos.h
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_MILOS_H
+#define LASTEXPRESS_MILOS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Milos : public Entity {
+public:
+ Milos(LastExpressEngine *engine);
+ ~Milos() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION_2(enterCompartmentDialog, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_1(function11, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION_1(function26, TimeValue timeValue)
+ DECLARE_FUNCTION_2(function27, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function35)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MILOS_H
diff --git a/engines/lastexpress/entities/mmeboutarel.cpp b/engines/lastexpress/entities/mmeboutarel.cpp
new file mode 100644
index 0000000000..aeaa1e631e
--- /dev/null
+++ b/engines/lastexpress/entities/mmeboutarel.cpp
@@ -0,0 +1,1301 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/mmeboutarel.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+MmeBoutarel::MmeBoutarel(LastExpressEngine *engine) : Entity(engine, kEntityMmeBoutarel) {
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, reset);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, playSound);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, draw);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, updateFromTime);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, updateEntity);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function8);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function9);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter1);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function11);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function13);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function14);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function15);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function16);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter2);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function19);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter3);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter4);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function24);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function25);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter5);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function28);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, MmeBoutarel, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, MmeBoutarel, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, MmeBoutarel, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, MmeBoutarel, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, MmeBoutarel, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, MmeBoutarel, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_5790, kPosition_6130, kCarRedSleeping, kObjectCompartmentD, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, MmeBoutarel, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getInventory()->hasItem(kItemPassengerList) ? getSound()->playSound(kEntityPlayer, "CAT1021") : getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(8, MmeBoutarel, function8)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && params->param5) {
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ params->param5 = 1;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606U");
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction169557824);
+ break;
+
+ case kAction155853632:
+ params->param4 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606L");
+ getSound()->playSound(kEntityMmeBoutarel, (char *)&params->seq1);
+
+ if (getEntities()->hasValidFrame(kEntityMmeBoutarel) || getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000)) {
+ if (getProgress().chapter == kChapter1)
+ getProgress().field_A8 = 1;
+ else if (getProgress().chapter == kChapter3)
+ getProgress().field_A4 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, MmeBoutarel, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ getData()->entityPosition = getEntityData(kEntityBoutarel)->entityPosition;
+ getData()->location = getEntityData(kEntityBoutarel)->location;
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityBoutarel, kAction203520448);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityFrancois, kCarRedSleeping, kPosition_5790)) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ } else {
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Md");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ }
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kAction100957716:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, MmeBoutarel, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMmeBoutarel, kAction242526416, 0);
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, MmeBoutarel, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 1000) || !params->param2)
+ params->param2 = (uint)getState()->time + 150;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("MME1040");
+ break;
+
+ case kActionDefault:
+ params->param1 = (uint)getState()->time + 1800;
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("MME1040A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("MME1041");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateFromTime(900);
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, MmeBoutarel, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 44)) {
+ setCallback(1);
+ setup_draw("502B");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Qd");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ params->param1 = 1;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ setup_function13();
+ break;
+ }
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param1 = 0;
+ break;
+
+ case kAction168986720:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction102752636);
+ getSound()->playSound(kEntityMmeBoutarel, "MME1036");
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+
+ setCallback(3);
+ setup_enterExitCompartment("606Fd", kObjectCompartmentD);
+ break;
+
+ case kAction202221040:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ getSound()->playSound(kEntityMmeBoutarel, "MME1035A");
+
+ if (getEntities()->hasValidFrame(kEntityMmeBoutarel) || getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000) )
+ getProgress().field_AC = 1;
+
+ setCallback(2);
+ setup_enterExitCompartment("606Ed", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, MmeBoutarel, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityMmeBoutarel) && params->param6 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000), params->param6, 0)
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000))
+ getProgress().field_A0 = 1;
+
+ params->param5 = 1;
+
+ setCallback(1);
+ setup_playSound("MME1037");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_1:
+ if (getProgress().field_24 && params->param7 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(kTime1093500, (!params->param5 || !getEntities()->isPlayerInCar(kCarRedSleeping)), params->param7, 0)
+ setCallback(2);
+ setup_function11();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ TIME_CHECK(kTime1094400, params->param8, setup_function14);
+
+ if (params->param4) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75);
+
+ params->param3 = 1;
+ params->param4 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ CURRENT_PARAM(1, 1) = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param4) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? "CAT1510" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ ++params->param2;
+
+ setCallback(savepoint.action == kActionKnock ? 4 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = (uint)getState()->time + 900;
+ getData()->entityPosition = kPosition_5790;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param4) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ params->param4 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback_1;
+
+ case 2:
+ setup_function14();
+ break;
+
+ case 3:
+ case 4:
+ setCallback(params->param2 <= 1 ? 6 : 5);
+ setup_playSound(params->param2 <= 1 ? "MME1038" : "MME1038C");
+ break;
+
+ case 5:
+ case 6:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param4 = 1;
+ break;
+
+ case 7:
+ case 8:
+ params->param3 = 1;
+ params->param4 = 0;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, MmeBoutarel, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("606Dd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "503");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "503");
+
+ setCallback(3);
+ setup_playSound("MRB1080");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ setup_function15();
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, MmeBoutarel, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTimeEnterChalons && !params->param4) {
+ params->param4 = 1;
+
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+ }
+
+label_callback_5:
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 1;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(10);
+ setup_playSound(rnd(2) ? "CAT1510" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(11);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ break;
+ }
+
+ ++params->param3;
+
+ setCallback(savepoint.action == kActionKnock ? 7 : 6);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param3 = 0; // BUG" why param3 when it's always param2?
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function8("MME1101");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("606Td", kObjectCompartmentD);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback_5;
+
+ case 6:
+ case 7:
+ if (params->param3 <= 1) {
+ setCallback(9);
+ setup_playSound("MME1038");
+ } else {
+ setCallback(8);
+ setup_playSound("MME1038C");
+ }
+ break;
+
+ case 8:
+ case 9:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 10:
+ case 11:
+ params->param1 = 1;
+ params->param2 = 0;
+ break;
+
+ case 12:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction223068211:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(12);
+ setup_playSound("MME1151B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, MmeBoutarel, function16)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, MmeBoutarel, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject43, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, MmeBoutarel, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isInsideCompartment(kEntityFrancois, kCarRedSleeping, kPosition_5790)) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Md");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kAction100957716:
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, MmeBoutarel, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 44) && !params->param2) {
+ if (params->param1) {
+ setCallback(1);
+ setup_draw("502B");
+ } else {
+ params->param1 = 1;
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping , 44))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 11);
+ }
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param2 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param2 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, MmeBoutarel, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, MmeBoutarel, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1) && params->param2 != kTimeInvalid) {
+
+ if (getState()->time <= kTime2038500) {
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping)
+ || !params->param1
+ || getSound()->isBuffered("FRA2012")
+ || getSound()->isBuffered("FRA2010")
+ ||!params->param2)
+ params->param2 = (uint)getState()->time;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction189872836);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject43, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function8("MME3001");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment2("606Td", kObjectCompartmentD);
+ break;
+
+ case 6:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(7);
+ setup_updateFromTime(150);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("606Dd", kObjectCompartmentD);
+ break;
+
+ case 8:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction190390860);
+ break;
+
+ case 9:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(9);
+ setup_function9();
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, MmeBoutarel, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, MmeBoutarel, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->time, 900);
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ setup_function24();
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, MmeBoutarel, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2470500, params->param4, setup_function25);
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 1;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1510" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ ++params->param3;
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(params->param3 > 1 ? 3 : 4);
+ setup_playSound(params->param3 > 1 ? "MME1038C" : "MME1038");
+ break;
+
+ case 3:
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 5:
+ case 6:
+ params->param1 = 1;
+ params->param2 = 0;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction123199584);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction88652208);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ setCallback(8);
+ setup_playSound("Mme1151A");
+ break;
+
+ case kAction221683008:
+ setCallback(7);
+ setup_playSound("Mme1038");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, MmeBoutarel, function25)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, MmeBoutarel, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, MmeBoutarel, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function28();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, MmeBoutarel, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param3 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound("Mme5001");
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction155604840:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(29, MmeBoutarel)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mmeboutarel.h b/engines/lastexpress/entities/mmeboutarel.h
new file mode 100644
index 0000000000..1f1d762dd9
--- /dev/null
+++ b/engines/lastexpress/entities/mmeboutarel.h
@@ -0,0 +1,164 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_MMEBOUTAREL_H
+#define LASTEXPRESS_MMEBOUTAREL_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class MmeBoutarel : public Entity {
+public:
+ MmeBoutarel(LastExpressEngine *engine);
+ ~MmeBoutarel() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function8, const char *soundName)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function28)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MMEBOUTAREL_H
diff --git a/engines/lastexpress/entities/pascale.cpp b/engines/lastexpress/entities/pascale.cpp
new file mode 100644
index 0000000000..2c8c29177b
--- /dev/null
+++ b/engines/lastexpress/entities/pascale.cpp
@@ -0,0 +1,1232 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/pascale.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Pascale::Pascale(LastExpressEngine *engine) : Entity(engine, kEntityPascale) {
+ ADD_CALLBACK_FUNCTION(Pascale, draw);
+ ADD_CALLBACK_FUNCTION(Pascale, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Pascale, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Pascale, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Pascale, updatePosition);
+ ADD_CALLBACK_FUNCTION(Pascale, playSound);
+ ADD_CALLBACK_FUNCTION(Pascale, draw2);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeSophieAndRebecca);
+ ADD_CALLBACK_FUNCTION(Pascale, sitSophieAndRebecca);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeCath);
+ ADD_CALLBACK_FUNCTION(Pascale, function11);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter1);
+ ADD_CALLBACK_FUNCTION(Pascale, getMessageFromAugustToTyler);
+ ADD_CALLBACK_FUNCTION(Pascale, sitAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, serveTatianaVassili);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function18);
+ ADD_CALLBACK_FUNCTION(Pascale, function19);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter2);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter3);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function23);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeAbbot);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter4);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function27);
+ ADD_CALLBACK_FUNCTION(Pascale, messageFromAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, function29);
+ ADD_CALLBACK_FUNCTION(Pascale, function30);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter5);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function33);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(1, Pascale, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Pascale, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Pascale, callbackActionOnDirection)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityPascale);
+ params->param1 = 1;
+ }
+
+ return;
+ }
+
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Pascale, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Pascale, updatePosition)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Pascale, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Pascale, draw2)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Pascale, welcomeSophieAndRebecca)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("901");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getSound()->playSound(kEntityPascale, "REB1198", SoundManager::kFlagInvalid, 30);
+ break;
+
+ case kChapter3:
+ getSound()->playSound(kEntityPascale, "REB3001", SoundManager::kFlagInvalid, 30);
+ break;
+
+ case kChapter4:
+ getSound()->playSound(kEntityPascale, "REB4001", SoundManager::kFlagInvalid, 30);
+ break;
+ }
+
+ setCallback(2);
+ setup_sitSophieAndRebecca();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityRebecca, kAction157370960);
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Pascale, sitSophieAndRebecca)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityPascale, "012C1");
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012C2");
+ getEntities()->drawSequenceLeft(kEntityTables3, "012C3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Pascale, welcomeCath)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && !getSound()->isBuffered(kEntityPascale))
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 64);
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param2) {
+ params->param2 = 1;
+
+ getSound()->playSound(kEntityPascale, "HED1001A");
+ getSound()->playSound(kEntityPlayer, "LIB004");
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction4:
+ if (!params->param1) {
+ params->param1 = 1;
+ getSound()->playSound(kEntityPascale, "HED1001");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 64);
+ getEntities()->drawSequenceRight(kEntityPascale, "035A");
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isPlayerPosition(kCarRestaurant, 64)) {
+ getSound()->playSound(kEntityPascale, "HED1001A");
+ getSound()->playSound(kEntityPlayer, "LIB004");
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Pascale, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction168046720);
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction168046720);
+ getSavePoints()->push(kEntityPascale, kEntityAlexei, kAction168046720);
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 55);
+
+ setCallback(1);
+ setup_welcomeCath();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction168627977);
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction168627977);
+ getSavePoints()->push(kEntityPascale, kEntityAlexei, kAction168627977);
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 55);
+
+ setCallback(2);
+ setup_draw("905");
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Pascale, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityPascale, kAction239072064, 0);
+ getSavePoints()->addData(kEntityPascale, kAction257489762, 2);
+ getSavePoints()->addData(kEntityPascale, kAction207769280, 6);
+ getSavePoints()->addData(kEntityPascale, kAction101824388, 7);
+ getSavePoints()->addData(kEntityPascale, kAction136059947, 8);
+ getSavePoints()->addData(kEntityPascale, kAction223262556, 1);
+ getSavePoints()->addData(kEntityPascale, kAction269479296, 3);
+ getSavePoints()->addData(kEntityPascale, kAction352703104, 4);
+ getSavePoints()->addData(kEntityPascale, kAction352768896, 5);
+ getSavePoints()->addData(kEntityPascale, kAction191604416, 10);
+ getSavePoints()->addData(kEntityPascale, kAction190605184, 11);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Pascale, getMessageFromAugustToTyler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("902");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!ENTITY_PARAM(1, 3)) {
+ getEntities()->drawSequenceLeft(kEntityPascale, "010E");
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+
+ setCallback(2);
+ setup_playSound("AUG1001");
+ break;
+ }
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityPascale, "010B");
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityPascale);
+ getSavePoints()->push(kEntityPascale, kEntityVerges, kActionDeliverMessageToTyler);
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Pascale, sitAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 62);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables0, "001C3");
+ getEntities()->drawSequenceRight(kEntityAnna, "001C2");
+ getEntities()->drawSequenceRight(kEntityPascale, "001C1");
+
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 62);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Pascale, welcomeAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("901");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPascale, "ANN1047");
+
+ setCallback(2);
+ setup_sitAnna();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction157370960);
+
+ setCallback(3);
+ setup_draw("904");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Pascale, serveTatianaVassili)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("903");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityTatiana, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityPascale, "014B");
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 67);
+
+ if (getSound()->isBuffered("TAT1069A"))
+ getSound()->processEntry("TAT1069A");
+ else if (getSound()->isBuffered("TAT1069B"))
+ getSound()->processEntry("TAT1069B");
+
+ setCallback(2);
+ setup_playSound("TAT1066");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityPascale, kEntityTatiana, kAction122288808);
+
+ setCallback(3);
+ setup_draw("906");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Pascale, chapter1Handler)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61))
+ params->param1 = 1;
+ }
+
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (ENTITY_PARAM(0, 5) && ENTITY_PARAM(0, 6)) {
+ setup_function18();
+ break;
+ }
+
+ if (!getEntities()->isSomebodyInsideRestaurantOrSalon())
+ goto label_callback3;
+
+ if (params->param1 && !params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61)) {
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(0, 1) && !ENTITY_PARAM(1, 3)) {
+ setCallback(2);
+ setup_getMessageFromAugustToTyler();
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_serveTatianaVassili();
+ break;
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(4);
+ setup_welcomeAnna();
+ break;
+ }
+
+label_callback4:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(5);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Pascale, function18)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (getState()->time > kTime1242000 && !params->param1) {
+ params->param1 = 1;
+
+ getSavePoints()->push(kEntityPascale, kEntityServers0, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityServers1, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityCooks, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityVerges, kAction101632192);
+
+ setup_function19();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Pascale, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntityData(kEntityPlayer)->entityPosition < kPosition_3650) {
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityPascale, kEntityTables0, kActionDrawTablesWithChairs, "001P");
+ getSavePoints()->push(kEntityPascale, kEntityTables1, kActionDrawTablesWithChairs, "005J");
+ getSavePoints()->push(kEntityPascale, kEntityTables2, kActionDrawTablesWithChairs, "009G");
+ getSavePoints()->push(kEntityPascale, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kActionDrawTablesWithChairs, "014F");
+ getSavePoints()->push(kEntityPascale, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityPascale);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Pascale, chapter2)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Pascale, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Pascale, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(1);
+ setup_function23();
+ break;
+ }
+
+label_callback:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(2);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ goto label_callback;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Pascale, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_welcomeAbbot();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityPascale, kEntityAbbot, kAction122288808);
+
+ setCallback(2);
+ setup_draw("906");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 7) = 0;
+ getEntities()->clearSequences(kEntityPascale);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Pascale, welcomeAbbot)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ getSound()->playSound(kEntityPascale, "ABB3015A");
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kAction10:
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kAction136455232);
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityPascale, "ABB3015", SoundManager::kFlagInvalid, 105);
+ getEntities()->drawSequenceRight(kEntityPascale, "029A1");
+ getEntities()->drawSequenceRight(kEntityAbbot, "029A2");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Pascale, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Pascale, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2511000 && !params->param4) {
+ params->param2 = 1;
+ params->param4 = 1;
+ }
+
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function27();
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(1, 2) && ENTITY_PARAM(1, 4)) {
+ if (!params->param3)
+ params->param3 = (uint)(getState()->time + 9000);
+
+ if (params->param5 != kTimeInvalid) {
+
+ if (params->param3 < getState()->time) {
+ params->param5 = kTimeInvalid;
+ setCallback(2);
+ setup_messageFromAnna();
+ break;
+ }
+
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time;
+
+ if (params->param5 < getState()->time) {
+ params->param5 = kTimeInvalid;
+ setCallback(2);
+ setup_messageFromAnna();
+ break;
+ }
+ }
+ }
+
+label_callback2:
+ if (params->param1 && !params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61)) {
+ setCallback(3);
+ setup_function11();
+ break;
+ }
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+ break;
+
+ case kActionDrawScene:
+ if (!params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61))
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback3;
+ }
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ getSavePoints()->push(kEntityPascale, kEntityTables0, kActionDrawTablesWithChairs, "001P");
+ getSavePoints()->push(kEntityPascale, kEntityTables1, kActionDrawTablesWithChairs, "005J");
+ getSavePoints()->push(kEntityPascale, kEntityTables2, kActionDrawTablesWithChairs, "009G");
+ getSavePoints()->push(kEntityPascale, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kActionDrawTablesWithChairs, "014F");
+ getSavePoints()->push(kEntityPascale, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Pascale, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_updateFromTime(450);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function29();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityPascale);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityCoudert, kAction123712592);
+
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function30();
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 8) = 0;
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Pascale, messageFromAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("902");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityPascale, "010E2");
+
+ setCallback(2);
+ setup_playSound("Aug4001");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction123793792);
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(1, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Pascale, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("817DD");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityPascale, "817DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityPascale);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_850;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Pascale, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("817US");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityPascale, "817UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityPascale);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Pascale, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Pascale, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function33();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Pascale, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4) {
+ UPDATE_PARAM_PROC(params->param5, getState()->time, 4500)
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("Wat5010");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback1:
+ if (params->param1) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 2;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param6 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(2);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param1) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback1;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ params->param3++;
+
+ if (params->param3 == 1 || params->param3 == 2) {
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ setCallback(params->param3 == 1 ? 5 : 6);
+ setup_playSound(params->param3 == 1 ? "Wat5001" : "Wat5002");
+ }
+ break;
+
+ case 5:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 6:
+ params->param2 = 1;
+ break;
+
+ case 7:
+ params->param4 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction169750080:
+ if (getSound()->isBuffered(kEntityPascale)) {
+ params->param4 = 1;
+ } else {
+ setCallback(7);
+ setup_playSound("Wat5002");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(34, Pascale)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/pascale.h b/engines/lastexpress/entities/pascale.h
new file mode 100644
index 0000000000..d0098dcf0b
--- /dev/null
+++ b/engines/lastexpress/entities/pascale.h
@@ -0,0 +1,165 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_PASCALE_H
+#define LASTEXPRESS_PASCALE_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Pascale : public Entity {
+public:
+ Pascale(LastExpressEngine *engine);
+ ~Pascale() {}
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the position
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The car
+ * - The position
+ */
+ DECLARE_FUNCTION_NOSETUP(updatePosition)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The sequence to draw for the second entity
+ * - The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_NOSETUP(draw2)
+
+ DECLARE_FUNCTION(welcomeSophieAndRebecca)
+ DECLARE_FUNCTION(sitSophieAndRebecca)
+ DECLARE_FUNCTION(welcomeCath)
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(getMessageFromAugustToTyler)
+ DECLARE_FUNCTION(sitAnna)
+ DECLARE_FUNCTION(welcomeAnna)
+ DECLARE_FUNCTION(serveTatianaVassili)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(welcomeAbbot)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(messageFromAnna)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function33)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_PASCALE_H
diff --git a/engines/lastexpress/entities/rebecca.cpp b/engines/lastexpress/entities/rebecca.cpp
new file mode 100644
index 0000000000..e902c5f37b
--- /dev/null
+++ b/engines/lastexpress/entities/rebecca.cpp
@@ -0,0 +1,1838 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/rebecca.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Rebecca::Rebecca(LastExpressEngine *engine) : Entity(engine, kEntityRebecca) {
+ ADD_CALLBACK_FUNCTION(Rebecca, reset);
+ ADD_CALLBACK_FUNCTION(Rebecca, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Rebecca, playSound);
+ ADD_CALLBACK_FUNCTION(Rebecca, playSound16);
+ ADD_CALLBACK_FUNCTION(Rebecca, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Rebecca, draw);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(Rebecca, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Rebecca, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Rebecca, updateEntity);
+ ADD_CALLBACK_FUNCTION(Rebecca, updatePosition);
+ ADD_CALLBACK_FUNCTION(Rebecca, draw2);
+ ADD_CALLBACK_FUNCTION(Rebecca, function15);
+ ADD_CALLBACK_FUNCTION(Rebecca, function16);
+ ADD_CALLBACK_FUNCTION(Rebecca, function17);
+ ADD_CALLBACK_FUNCTION(Rebecca, function18);
+ ADD_CALLBACK_FUNCTION(Rebecca, function19);
+ ADD_CALLBACK_FUNCTION(Rebecca, function20);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter1);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function23);
+ ADD_CALLBACK_FUNCTION(Rebecca, function24);
+ ADD_CALLBACK_FUNCTION(Rebecca, function25);
+ ADD_CALLBACK_FUNCTION(Rebecca, function26);
+ ADD_CALLBACK_FUNCTION(Rebecca, function27);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter2);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function30);
+ ADD_CALLBACK_FUNCTION(Rebecca, function31);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter3);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function34);
+ ADD_CALLBACK_FUNCTION(Rebecca, function35);
+ ADD_CALLBACK_FUNCTION(Rebecca, function36);
+ ADD_CALLBACK_FUNCTION(Rebecca, function37);
+ ADD_CALLBACK_FUNCTION(Rebecca, function38);
+ ADD_CALLBACK_FUNCTION(Rebecca, function39);
+ ADD_CALLBACK_FUNCTION(Rebecca, function40);
+ ADD_CALLBACK_FUNCTION(Rebecca, function41);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter4);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function44);
+ ADD_CALLBACK_FUNCTION(Rebecca, function45);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter5);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function48);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Rebecca, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, Rebecca, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Rebecca, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Rebecca, playSound16)
+ Entity::playSound(savepoint, false, getSound()->getSoundFlag(kEntityCoudert));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(5, Rebecca, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Rebecca, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, Rebecca, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(8, Rebecca, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_4840, kPosition_4455, kCarRedSleeping, kObjectCompartmentE, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(9, Rebecca, enterExitCompartment3, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Rebecca, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Rebecca, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(12, Rebecca, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(13, Rebecca, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(14, Rebecca, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Rebecca, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isOutsideAnnaWindow())
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ setCallback(1);
+ setup_enterExitCompartment2("624Ae", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getObjects()->update(kObjectOutsideBetweenCompartments, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityRebecca);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Rebecca, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)) {
+ if (!getEntities()->hasValidFrame(kEntitySophie)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ }
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment(params->param1 ? "624Be" : "623Ee", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityRebecca);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("810US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityRebecca, "012B");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityRebecca);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012A");
+ if (getProgress().chapter == kChapter3)
+ getSound()->playSound(kEntityRebecca, "REB3000");
+
+ getSavePoints()->push(kEntityRebecca, kEntityPascale, kAction269479296);
+
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction157370960:
+ getSavePoints()->push(kEntityRebecca, kEntityTables3, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ && !getEntities()->hasValidFrame(kEntitySophie)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(3);
+ setup_updateFromTime(0);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("624Be", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getProgress().chapter == kChapter3)
+ getSound()->playSound(kEntityRebecca, "Reb3005", SoundManager::kFlagInvalid, 75);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_updatePosition("118A", kCarRestaurant, 52);
+ } else {
+ getEntities()->updatePositionEnter(kEntityRebecca, kCarRestaurant, 57);
+
+ setCallback(6);
+ setup_draw2("107A1", "107A2", kEntitySophie);
+ }
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->updatePositionExit(kEntityRebecca, kCarRestaurant, 57);
+ getEntities()->clearSequences(kEntitySophie);
+
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Rebecca, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ getEntities()->exitCompartment(kEntityRebecca, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_function15();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction136654208);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(2);
+ setup_function15();
+ } else {
+ getEntities()->drawSequenceLeft(kEntityRebecca, "623Ge");
+ getEntities()->enterCompartment(kEntityRebecca, kObjectCompartmentE, true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Rebecca, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ getEntities()->exitCompartment(kEntityRebecca, kObjectCompartmentE, true);
+
+ setCallback(6);
+ setup_function15();
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_callSavepoint("012H", kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction337548856);
+ getEntities()->drawSequenceRight(kEntityRebecca, "810DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityRebecca);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction136654208);
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 4:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(5);
+ setup_function15();
+ } else {
+ getEntities()->drawSequenceLeft(kEntityRebecca, "623Ge");
+ getEntities()->enterCompartment(kEntityRebecca, kObjectCompartmentE, true);
+ }
+ break;
+
+ case 5:
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(20, Rebecca, function20, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param5) {
+ params->param5 = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!params->param2) {
+ params->param6 = 0;
+ } else {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75)
+ params->param2 = 0;
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getProgress().chapter == kChapter1 && !ENTITY_PARAM(0, 3)) {
+ if (params->param7 != kTimeInvalid && getState()->time > kTime1174500) {
+ if (getState()->time <= kTime1183500) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 2000) || getSound()->isBuffered("CON1210") || !params->param7)
+ params->param7 = (uint)(getState()->time);
+
+ if (params->param7 >= getState()->time)
+ goto label_callback;
+ }
+
+ params->param7 = kTimeInvalid;
+ ENTITY_PARAM(0, 3) = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("REB1205");
+ break;
+ }
+ goto label_callback;
+ }
+
+ if (getProgress().chapter == kChapter3 && !ENTITY_PARAM(0, 4) && params->param8 != kTimeInvalid && getState()->time > kTime2097000) {
+ if (getState()->time <= kTime2106000) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 1000) || !params->param8)
+ params->param8 = (uint)getState()->time;
+
+ if (params->param8 >= getState()->time)
+ goto label_callback;
+ }
+
+ params->param8 = kTimeInvalid;
+ ENTITY_PARAM(0, 4) = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound("REB3010");
+ break;
+ }
+
+label_callback:
+ if (ENTITY_PARAM(0, 2) && getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 1000)) {
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_playSound("REB1040");
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getCallback() != 2)
+ ENTITY_PARAM(0, 2) = 0;
+
+ if (getCallback() != 3)
+ goto label_callback;
+ break;
+
+ case 4:
+ case 5:
+ if (rnd(2)) {
+ setCallback(6);
+ setup_playSound("REB1039");
+ } else {
+ setCallback(7);
+ setup_playSound(rnd(2) ? "SOP1039" : "SOP1039A");
+ }
+ break;
+
+ case 6:
+ case 7:
+ params->param4 = (getCallback() == 6 ? 0 : 1);
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_playSound16("JAC1012B");
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction254915200:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(12);
+ setup_playSound("REB1039A");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Rebecca, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityRebecca, kAction224253538, 0);
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideBetweenCompartments, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation1);
+
+ getData()->entityPosition = kPosition_2830;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1084500, params->param3, 1, setup_playSound, "REB1015");
+
+ if (params->param4 == kTimeInvalid)
+ goto label_callback_4;
+
+ if (getState()->time > kTime1080000)
+ goto label_playConversation;
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)(getState()->time + 150);
+
+ if (params->param4 >= getState()->time) {
+label_callback_4:
+ if (params->param1) {
+ UPDATE_PARAM_CHECK(params->param5, getState()->time, 900)
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setCallback(5);
+ setup_playSound("REB1013");
+ break;
+ }
+ }
+ }
+
+label_callback_5:
+ if (params->param2) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param6 = 0;
+ }
+ } else {
+label_playConversation:
+ params->param4 = kTimeInvalid;
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getProgress().field_B8 = 1;
+
+ setCallback(4);
+ setup_playSound("REB1012");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "107B");
+ break;
+
+ case kActionDrawScene:
+ params->param2 = (getEntities()->isPlayerPosition(kCarRestaurant, 57) ? 1 : 0);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updatePosition("107C", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ setup_function23();
+ break;
+
+ case 4:
+ params->param1 = 1;
+ goto label_callback_4;
+
+ case 5:
+ getProgress().field_B4 = 1;
+ params->param1 = 0;
+ goto label_callback_5;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Rebecca, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_2(kTime1111500, params->param2, 3, setup_enterExitCompartment, "623De", kObjectCompartmentE);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(900);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("623Ce", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "504");
+ break;
+
+ case 3:
+ case 6:
+ getEntities()->clearSequences(kEntityRebecca);
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback((byte)(getCallback() + 1));
+ setup_function20(kTime1120500);
+ break;
+
+ case 4:
+ case 5:
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function24();
+ } else {
+ setCallback(5);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ break;
+
+ case 7:
+ case 8:
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function24();
+ } else {
+ setCallback(8);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ break;
+ }
+ break;
+
+ case kAction285528346:
+ setCallback(6);
+ setup_enterExitCompartment("623De", kObjectCompartmentE);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Rebecca, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416);
+
+ if (!params->param1)
+ break;
+
+ TIME_CHECK_CALLBACK(kTime1165500, params->param3, 6, setup_function19);
+
+ if (params->param4 != kTimeInvalid) {
+ if (getState()->time <= kTime1161000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 150;
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setCallback(7);
+ setup_playSound("REB1200A");
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+
+ setCallback(2);
+ setup_playSound("REB1199");
+ break;
+
+ case 2:
+ if (getEntities()->isInRestaurant(kEntityPlayer)) {
+ setCallback(3);
+ setup_playSound("REB1199A");
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ if (getCallback() == 3)
+ getProgress().field_BC = 1;
+
+ if (getEntities()->isInRestaurant(kEntityAnna)) {
+ setCallback(4);
+ setup_playSound("REB1199B");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ setCallback(5);
+ setup_playSound("REB1199C");
+ break;
+
+ case 6:
+ setup_function25();
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012E");
+
+ setCallback(8);
+ setup_playSound("REB1200");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Rebecca, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime1184400);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Rebecca, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_3(kTime1224000, params->param2, 1, setup_updatePosition, "118H", kCarRestaurant, 52);
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 52);
+ params->param3 = 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Rebecca, function27)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityRebecca);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Rebecca, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation2);
+
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Rebecca, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime1764000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Rebecca, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && params->param4 != kTimeInvalid) {
+
+ if (getState()->time <= kTimeEnd)
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 450;
+
+ if (params->param4 < getState()->time || getState()->time > kTimeEnd) {
+ params->param4 = kTimeInvalid;
+
+ getSound()->playSound(kEntityRebecca, "Reb2001");
+ getProgress().field_B0 = 1;
+ params->param2 = 1;
+ }
+ }
+
+ if (!params->param3 && !params->param2 && params->param5 != kTimeInvalid) {
+
+ if (getState()->time <= kTime10881000) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time + 450;
+
+ if (params->param5 >= getState()->time)
+ break;
+ }
+
+ params->param5 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityAugust, kAction169358379);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "107B");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function31();
+ break;
+
+ case kAction125496184:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kAction155465152:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "BLANK");
+ break;
+
+ case kAction155980128:
+ params->param1 = 1;
+ params->param3 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Rebecca, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(900);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("623CE", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "504");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Rebecca, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Rebecca, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2016000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Rebecca, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid) {
+ if (getState()->time <= kTime1386000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param2)
+ params->param2 = (uint)getState()->time;
+
+ if (params->param2 >= getState()->time) {
+ TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19);
+ break;
+ }
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416);
+ }
+
+ TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19);
+ break;
+
+ case kActionEndSound:
+ setCallback(5);
+ setup_playSound("Reb3004");
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(2);
+ setup_playSound("Reb3002");
+ break;
+
+ case 3:
+ setup_function35();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getSound()->playSound(kEntityRebecca, "Reb3003");
+
+ setCallback(4);
+ setup_draw("012E");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Rebecca, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2070000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function36();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Rebecca, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2)
+ params->param2 = (uint)getState()->time + 1800;
+
+ if (params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+
+ if (getState()->time <= kTime2083500) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 300;
+ }
+
+ if (params->param4 < getState()->time || getState()->time > kTime2083500) {
+ params->param4 = kTimeInvalid;
+ getSound()->playSound(kEntityRebecca, "Reb3007");
+
+ setCallback(2);
+ setup_updatePosition("118E", kCarRedSleeping, 52);
+ break;
+ }
+ }
+
+ // TODO rewrite using proper if/else blocks instead of goto
+label_callback_2:
+ if (!params->param1)
+ goto label_callback_3;
+
+ if (!params->param3)
+ params->param3 = (uint)getState()->time + 9000;
+
+ if (params->param5 == kTimeInvalid || params->param3 >= getState()->time)
+ goto label_callback_3;
+
+ if (getState()->time <= kTime2092500) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time + 300;
+
+ if (params->param5 >= getState()->time) {
+label_callback_3:
+ if (getState()->time > kTime2097000 && !params->param6) {
+ params->param6 = 1;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_updatePosition("118H", kCarRestaurant, 52);
+ }
+ break;
+ }
+ }
+
+ params->param5 = kTimeInvalid;
+
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityRebecca, "Reb3008", SoundManager::kFlagInvalid, 60);
+ getEntities()->updatePositionEnter(kEntityRebecca, kCarRestaurant, 52);
+
+ setCallback(3);
+ setup_draw2("118G1", "118G2", kEntitySophie);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_playSound("SOP3008");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ break;
+
+ case 2:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemInvalid;
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118F");
+ goto label_callback_2;
+
+ case 3:
+ getEntities()->clearSequences(kEntitySophie);
+ getEntities()->updatePositionExit(kEntityRebecca, kCarRestaurant, 52);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ goto label_callback_3;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Rebecca, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2110500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function38();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Rebecca, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment3("624Be", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction259921280);
+
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ setup_function39();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Rebecca, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function40();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Rebecca, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_9270;
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction292775040);
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntityAnna, kAction191668032);
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ setCallback(4);
+ setup_function15();
+ break;
+
+ case 4:
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Rebecca, function41)
+ if (savepoint.action == kActionDefault) {
+ ENTITY_PARAM(0, 2) = 1;
+
+ setCallback(1);
+ setup_function20(kTimeEnd);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Rebecca, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation3);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Rebecca, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2385000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function44();
+ } else {
+ setCallback(2);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Rebecca, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 != kTimeInvalid) {
+ if (getState()->time <= kTime2412000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time;
+
+ if (params->param3 >= getState()->time)
+ goto label_next;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416);
+ }
+
+label_next:
+ if (params->param1 && params->param4 != kTimeInvalid) {
+ if (getState()->time <= kTime2430000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 150;
+
+ if (params->param4 >= getState()->time)
+ goto label_callback_2;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setCallback(2);
+ setup_playSound("Reb4004");
+ break;
+ }
+
+label_callback_2:
+ if (params->param2)
+ TIME_CHECK_CALLBACK(kTime2443500, params->param5, 3, setup_function19);
+ break;
+
+ case kActionEndSound:
+ if (getEntities()->isInRestaurant(kEntityPlayer)) {
+ setCallback(5);
+ setup_playSound("Reb4004");
+ break;
+ }
+
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ setup_function45();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "BLANK");
+ getSound()->playSound(kEntityRebecca, "Reb4003");
+
+ setCallback(4);
+ setup_draw("012E");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Rebecca, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityRebecca);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 1;
+ break;
+
+ case kAction205034665:
+ if (!params->param1 && getState()->time < kTime2511000) {
+ setCallback(1);
+ setup_playSound("Reb6969");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Rebecca, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Rebecca, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function48();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Rebecca, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param3 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ params->param1 = 0;
+
+ setCallback(2);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_enterExitCompartment("624AE", kObjectCompartmentE);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityRebecca);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4840;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("Reb5001");
+ break;
+
+ case 5:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction155604840:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(49, Rebecca)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/rebecca.h b/engines/lastexpress/entities/rebecca.h
new file mode 100644
index 0000000000..bdc9fe1d3b
--- /dev/null
+++ b/engines/lastexpress/entities/rebecca.h
@@ -0,0 +1,230 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_REBECCA_H
+#define LASTEXPRESS_REBECCA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Rebecca : public Entity {
+public:
+ Rebecca(LastExpressEngine *engine);
+ ~Rebecca() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment3, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION_1(function16, bool)
+ DECLARE_FUNCTION_1(function17, bool)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION_1(function20, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function48)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_REBECCA_H
diff --git a/engines/lastexpress/entities/salko.cpp b/engines/lastexpress/entities/salko.cpp
new file mode 100644
index 0000000000..cddbc9005d
--- /dev/null
+++ b/engines/lastexpress/entities/salko.cpp
@@ -0,0 +1,642 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/salko.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Salko::Salko(LastExpressEngine *engine) : Entity(engine, kEntitySalko) {
+ ADD_CALLBACK_FUNCTION(Salko, reset);
+ ADD_CALLBACK_FUNCTION(Salko, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Salko, draw);
+ ADD_CALLBACK_FUNCTION(Salko, updateEntity);
+ ADD_CALLBACK_FUNCTION(Salko, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Salko, savegame);
+ ADD_CALLBACK_FUNCTION(Salko, function7);
+ ADD_CALLBACK_FUNCTION(Salko, function8);
+ ADD_CALLBACK_FUNCTION(Salko, chapter1);
+ ADD_CALLBACK_FUNCTION(Salko, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function11);
+ ADD_CALLBACK_FUNCTION(Salko, chapter2);
+ ADD_CALLBACK_FUNCTION(Salko, function13);
+ ADD_CALLBACK_FUNCTION(Salko, chapter3);
+ ADD_CALLBACK_FUNCTION(Salko, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function16);
+ ADD_CALLBACK_FUNCTION(Salko, function17);
+ ADD_CALLBACK_FUNCTION(Salko, chapter4);
+ ADD_CALLBACK_FUNCTION(Salko, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function20);
+ ADD_CALLBACK_FUNCTION(Salko, function21);
+ ADD_CALLBACK_FUNCTION(Salko, function22);
+ ADD_CALLBACK_FUNCTION(Salko, chapter5);
+ ADD_CALLBACK_FUNCTION(Salko, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Salko, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Salko, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(3, Salko, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(4, Salko, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(5, Salko, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(6, Salko, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Salko, function7, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ params->param3 = 0;
+
+ EntityDirection direction = getData()->direction;
+ CarIndex carSalko = getData()->car;
+ CarIndex carIvo = getEntityData(kEntityIvo)->car;
+ EntityPosition positionSalko = getData()->entityPosition;
+ EntityPosition positionIvo = getEntityData(kEntityIvo)->entityPosition;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntitySalko, kEntityIvo, 500)
+ || (direction == kDirectionUp && (carSalko > carIvo || (carSalko == carIvo && positionSalko > positionIvo)))
+ || (direction == kDirectionDown && (carSalko < carIvo || (carSalko == carIvo && positionSalko < positionIvo)))) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntitySalko, (CarIndex)params->param1, (EntityPosition)params->param2);
+
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ getSound()->playSound(kEntityPlayer, "ZFX1002", getSound()->getSoundFlag(kEntitySalko));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntitySalko, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Salko, function8)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Salko, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Salko, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityIvo)->entityPosition;
+ getData()->location = getEntityData(kEntityIvo)->location;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->clearSequences(kEntitySalko);
+ setup_function8();
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Salko, function11)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntitySalko);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Salko, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction136184016:
+ setCallback(1);
+ setup_function13();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Salko, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("612DH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntitySalko, kEntityIvo, kAction102675536);
+ getEntities()->clearSequences(kEntitySalko);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntitySalko, "BLANK");
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function8();
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(3);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Salko, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Salko, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time < kTime2200500) {
+ UPDATE_PARAM(params->param1, getState()->time, 81000);
+
+ setCallback(1);
+ setup_function16();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Salko, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->hasValidFrame(kEntitySalko) && getEntities()->isDistanceBetweenEntities(kEntitySalko, kEntityPlayer, 5000)) {
+ getSavePoints()->push(kEntitySalko, kEntityMax, kAction158007856);
+
+ setCallback(3);
+ setup_updateFromTime(75);
+ break;
+ }
+
+label_callback3:
+ UPDATE_PARAM(params->param1, getState()->time, 4500);
+
+ getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("612DH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464);
+ goto label_callback3;
+
+ case 4:
+ getEntities()->exitCompartment(kEntitySalko, kObjectCompartmentF, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_2740;
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction101169464:
+ setCallback(4);
+ setup_enterExitCompartment("612Bf", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Salko, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySalko);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntitySalko, kEntityMilos, kAction157691176);
+
+ setup_chapter3Handler();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Salko, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_5420;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Salko, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntitySalko, "BLANK");
+
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function20();
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Salko, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySalko);
+ setup_function21();
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ setCallback(1);
+ setup_enterExitCompartment("612Dh", kObjectCompartmentH);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Salko, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2422800 && !params->param1) {
+ params->param1 = 1;
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 2:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Salko, function22)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntitySalko);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Salko, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Salko, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ getAction()->playAnimation(kEventCathSalkoTrainTopFight);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightSalko);
+
+ if (params->param1 == Fight::kFightEndWin) {
+ getState()->time = (TimeValue)(getState()->time + 1800);
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventCathSalkoTrainTopWin);
+ } else {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param1 == Fight::kFightEndLost);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathSalkoTrainTopWin);
+ getSavePoints()->push(kEntitySalko, kEntityVesna, kAction134427424);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 10);
+ setup_nullfunction();
+ break;
+ }
+ break;
+
+ case kAction167992577:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathSalkoTrainTopFight);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(25, Salko)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/salko.h b/engines/lastexpress/entities/salko.h
new file mode 100644
index 0000000000..f21724d88a
--- /dev/null
+++ b/engines/lastexpress/entities/salko.h
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SALKO_H
+#define LASTEXPRESS_SALKO_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Salko : public Entity {
+public:
+ Salko(LastExpressEngine *engine);
+ ~Salko() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION_2(function7, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function8)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SALKO_H
diff --git a/engines/lastexpress/entities/servers0.cpp b/engines/lastexpress/entities/servers0.cpp
new file mode 100644
index 0000000000..1f3f85bb8f
--- /dev/null
+++ b/engines/lastexpress/entities/servers0.cpp
@@ -0,0 +1,1039 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/servers0.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+#define HANDLE_TABLE(index, param, callback, function) \
+ if (ENTITY_PARAM(index, param)) { \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+Servers0::Servers0(LastExpressEngine *engine) : Entity(engine, kEntityServers0) {
+ ADD_CALLBACK_FUNCTION(Servers0, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Servers0, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Servers0, draw);
+ ADD_CALLBACK_FUNCTION(Servers0, updatePosition);
+ ADD_CALLBACK_FUNCTION(Servers0, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Servers0, playSound);
+ ADD_CALLBACK_FUNCTION(Servers0, function7);
+ ADD_CALLBACK_FUNCTION(Servers0, function8);
+ ADD_CALLBACK_FUNCTION(Servers0, function9);
+ ADD_CALLBACK_FUNCTION(Servers0, function10);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter1);
+ ADD_CALLBACK_FUNCTION(Servers0, function12);
+ ADD_CALLBACK_FUNCTION(Servers0, function13);
+ ADD_CALLBACK_FUNCTION(Servers0, function14);
+ ADD_CALLBACK_FUNCTION(Servers0, function15);
+ ADD_CALLBACK_FUNCTION(Servers0, function16);
+ ADD_CALLBACK_FUNCTION(Servers0, function17);
+ ADD_CALLBACK_FUNCTION(Servers0, function18);
+ ADD_CALLBACK_FUNCTION(Servers0, function19);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, function21);
+ ADD_CALLBACK_FUNCTION(Servers0, function22);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter2);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, function25);
+ ADD_CALLBACK_FUNCTION(Servers0, function26);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter3);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, augustAnnaDateOrder);
+ ADD_CALLBACK_FUNCTION(Servers0, function30);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter4);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, augustOrderSteak);
+ ADD_CALLBACK_FUNCTION(Servers0, augustServeDuck);
+ ADD_CALLBACK_FUNCTION(Servers0, function35);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter5);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(1, Servers0, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(2, Servers0, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Servers0, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Servers0, updatePosition)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Servers0, callbackActionOnDirection)
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityServers0);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Servers0, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Servers0, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ ENTITY_PARAM(0, 3) = 0;
+
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityServers0);
+ getSavePoints()->push(kEntityServers0, kEntityRebecca, kAction123712592);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction136702400:
+ setCallback(2);
+ setup_draw("913");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Servers0, function8)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "913", &ENTITY_PARAM(1, 2));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Servers0, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("915");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "029D");
+
+ setCallback(2);
+ setup_playSound(getProgress().chapter == kChapter3 ? "Abb3016" : "Abb4001");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122288808);
+
+ setCallback(3);
+ setup_draw("917");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Servers0, function10)
+ serveTable(savepoint, "916", kEntityTables4, "014E", "014F", "918", &ENTITY_PARAM(2, 3), false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Servers0, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityServers0, kAction270410280, 0);
+ getSavePoints()->addData(kEntityServers0, kAction304061224, 1);
+ getSavePoints()->addData(kEntityServers0, kAction252568704, 10);
+ getSavePoints()->addData(kEntityServers0, kAction286534136, 11);
+ getSavePoints()->addData(kEntityServers0, kAction218983616, 12);
+ getSavePoints()->addData(kEntityServers0, kAction218586752, 13);
+ getSavePoints()->addData(kEntityServers0, kAction207330561, 14);
+ getSavePoints()->addData(kEntityServers0, kAction286403504, 16);
+ getSavePoints()->addData(kEntityServers0, kAction218128129, 17);
+ getSavePoints()->addData(kEntityServers0, kAction270068760, 18);
+ getSavePoints()->addData(kEntityServers0, kAction223712416, 2);
+ getSavePoints()->addData(kEntityServers0, kAction237485916, 5);
+ getSavePoints()->addData(kEntityServers0, kAction188893625, 8);
+ getSavePoints()->addData(kEntityServers0, kAction204704037, 6);
+ getSavePoints()->addData(kEntityServers0, kAction292758554, 7);
+ getSavePoints()->addData(kEntityServers0, kAction337548856, 9);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Servers0, function12)
+ handleServer(savepoint, "907", kEntityAnna, kAction268773672, &ENTITY_PARAM(0, 1));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Servers0, function13)
+ handleServer(savepoint, "911", kEntityAugust, kAction268773672, &ENTITY_PARAM(0, 2), "010F");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Servers0, function14)
+ handleServer(savepoint, "908", kEntityAnna, kAction170016384, &ENTITY_PARAM(0, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Servers0, function15)
+ handleServer(savepoint, "912", kEntityAugust, kAction170016384, &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Servers0, function16)
+ serveTable(savepoint, "907", kEntityTables0, "001N", "001P", "909", &ENTITY_PARAM(0, 6));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Servers0, function17)
+ serveTable(savepoint, "915", kEntityTables4, "014E", "014F", "917", &ENTITY_PARAM(1, 1), true, false, 67);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Servers0, function18)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010H", "913", &ENTITY_PARAM(0, 7));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Servers0, function19)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "913", &ENTITY_PARAM(0, 8), true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Servers0, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 2700);
+ ENTITY_PARAM(0, 4) = 1;
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM_PROC(params->param4, getState()->time, 4500)
+ ENTITY_PARAM(0, 5) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->isInKitchen(kEntityServers0) && !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ HANDLE_TABLE(0, 1, 1, setup_function12);
+ HANDLE_TABLE(0, 2, 2, setup_function13);
+ HANDLE_TABLE(0, 3, 3, setup_function7);
+ HANDLE_TABLE(0, 4, 4, setup_function14);
+ HANDLE_TABLE(0, 5, 5, setup_function15);
+ HANDLE_TABLE(0, 6, 6, setup_function16);
+ HANDLE_TABLE(1, 1, 7, setup_function17);
+ HANDLE_TABLE(0, 7, 8, setup_function18);
+ HANDLE_TABLE(0, 8, 9, setup_function19);
+ HANDLE_TABLE(1, 2, 10, setup_function8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityServers0, kEntityPascale, kAction352703104);
+ setup_function21();
+ break;
+
+ case 11:
+ case 12:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+
+ if (getCallback() == 11)
+ params->param2 = 1;
+ else
+ params->param1 = 1;
+ break;
+
+ case 13:
+ case 14:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+ break;
+ }
+ break;
+
+ case kAction136702400:
+ setCallback(savepoint.entity2 == kEntityAnna ? 13 : 14);
+ setup_draw(savepoint.entity2 == kEntityAnna ? "909" : "913");
+ break;
+
+ case kAction203859488:
+ setCallback(savepoint.entity2 == kEntityAnna ? 11 : 12);
+ setup_draw(savepoint.entity2 == kEntityAnna ? "910" : "913");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Servers0, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ break;
+
+ case kAction101632192:
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Servers0, function22)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityServers0);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Servers0, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Servers0, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers0) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ HANDLE_TABLE(1, 3, 1, setup_function25);
+ HANDLE_TABLE(1, 4, 2, setup_function26);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ HANDLE_TABLE(1, 4, 2, setup_function26);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Servers0, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("957");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction219522616:
+ setCallback(2);
+ setup_draw("959");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Servers0, function26)
+ serveTable(savepoint, "957", kEntityTables0, "016E", "016D", "959", &ENTITY_PARAM(1, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Servers0, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ ENTITY_PARAM(2, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Servers0, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(1);
+ setup_augustAnnaDateOrder();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(2, 4)) {
+ setCallback(3);
+ setup_function30();
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(2, 3)) {
+ setCallback(4);
+ setup_function10();
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(5);
+ setup_function7();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(6);
+ setup_function8();
+ break;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Servers0, augustAnnaDateOrder)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAnna, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "026D");
+
+ setCallback(2);
+ setup_playSound("Ann3138");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAnna, kAction122288808);
+
+ setCallback(3);
+ setup_draw("913");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 5) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Servers0, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("916");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "029D");
+
+ setCallback(2);
+ setup_playSound("Abb3016a");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122288808);
+
+ setCallback(3);
+ setup_draw("918");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(2, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Servers0, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Servers0, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 3600)
+ ENTITY_PARAM(1, 8) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(1);
+ setup_augustOrderSteak();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 8)) {
+ setCallback(2);
+ setup_augustServeDuck();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(2, 1)) {
+ setCallback(3);
+ setup_function35();
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(4);
+ setup_function9();
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(2, 3)) {
+ setCallback(5);
+ setup_function10();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(6);
+ setup_function7();
+ break;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 1;
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+ }
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ params->param1 = 0;
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Servers0, augustOrderSteak)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityServers0, "010F3");
+ getEntities()->drawSequenceLeft(kEntityAugust, "010D3");
+
+ setCallback(2);
+ setup_playSound("AUG4002");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction122288808);
+
+ setCallback(3);
+ setup_draw("913");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 7) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Servers0, augustServeDuck)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("912");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction122358304);
+ getSound()->playSound(kEntityServers0, "AUG1053");
+
+ setCallback(2);
+ setup_draw("010G3");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction201964801);
+
+ setCallback(3);
+ setup_draw("914");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 8) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Servers0, function35)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "914", &ENTITY_PARAM(2, 1), false, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Servers0, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Servers0, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(38, Servers0)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Servers0::handleServer(const SavePoint &savepoint, const char *name, EntityIndex entity, ActionIndex action, uint *parameter, const char *name2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw(name);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ // Prepare or draw sequences depending of value of string
+ if (strcmp(name2, ""))
+ getEntities()->clearSequences(kEntityServers0);
+ else
+ getEntities()->drawSequenceLeft(kEntityServers0, name2);
+
+ getSavePoints()->push(kEntityServers0, entity, action);
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Servers0::serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, bool shouldUpdatePosition, bool pushSavepoint, Position position) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (shouldUpdatePosition) {
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ }
+
+ setCallback(1);
+ setup_draw(seq1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (position)
+ getEntities()->updatePositionEnter(kEntityServers0, kCarRestaurant, position);
+
+ getSavePoints()->push(kEntityServers0, entity, kAction136455232);
+
+ setCallback(2);
+ setup_callSavepoint(seq2, entity, kActionDrawTablesWithChairs, seq3);
+ break;
+
+ case 2:
+ if (position)
+ getEntities()->updatePositionExit(kEntityServers0, kCarRestaurant, position);
+
+ setCallback(3);
+ setup_draw(seq4);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+
+ // Special case for functions 19 & 35
+ if (pushSavepoint)
+ getSavePoints()->push(kEntityServers0, kEntityRebecca, kAction224253538);
+
+ getEntities()->clearSequences(kEntityServers0);
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/servers0.h b/engines/lastexpress/entities/servers0.h
new file mode 100644
index 0000000000..3e1bd21e0d
--- /dev/null
+++ b/engines/lastexpress/entities/servers0.h
@@ -0,0 +1,172 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SERVERS0_H
+#define LASTEXPRESS_SERVERS0_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Servers0 : public Entity {
+public:
+ Servers0(LastExpressEngine *engine);
+ ~Servers0() {}
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION_NOSETUP(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ DECLARE_FUNCTION(function7)
+ DECLARE_FUNCTION(function8)
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(augustAnnaDateOrder)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(augustOrderSteak)
+ DECLARE_FUNCTION(augustServeDuck)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void handleServer(const SavePoint &savepoint, const char *name, EntityIndex entity, ActionIndex action, uint *parameter, const char *name2 = "");
+ void serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, bool shouldUpdatePosition = true, bool pushSavepoint = false, Position position = 0);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SERVERS0_H
diff --git a/engines/lastexpress/entities/servers1.cpp b/engines/lastexpress/entities/servers1.cpp
new file mode 100644
index 0000000000..1afeeab894
--- /dev/null
+++ b/engines/lastexpress/entities/servers1.cpp
@@ -0,0 +1,787 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/servers1.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Servers1::Servers1(LastExpressEngine *engine) : Entity(engine, kEntityServers1) {
+ ADD_CALLBACK_FUNCTION(Servers1, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Servers1, draw);
+ ADD_CALLBACK_FUNCTION(Servers1, updatePosition);
+ ADD_CALLBACK_FUNCTION(Servers1, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Servers1, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Servers1, playSound);
+ ADD_CALLBACK_FUNCTION(Servers1, function7);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter1);
+ ADD_CALLBACK_FUNCTION(Servers1, function9);
+ ADD_CALLBACK_FUNCTION(Servers1, function10);
+ ADD_CALLBACK_FUNCTION(Servers1, function11);
+ ADD_CALLBACK_FUNCTION(Servers1, function12);
+ ADD_CALLBACK_FUNCTION(Servers1, function13);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function15);
+ ADD_CALLBACK_FUNCTION(Servers1, function16);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter2);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function19);
+ ADD_CALLBACK_FUNCTION(Servers1, function20);
+ ADD_CALLBACK_FUNCTION(Servers1, function21);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter3);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function24);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter4);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function27);
+ ADD_CALLBACK_FUNCTION(Servers1, function28);
+ ADD_CALLBACK_FUNCTION(Servers1, function29);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter5);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter5Handler);
+ ADD_NULL_FUNCTION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(1, Servers1, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Servers1, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(3, Servers1, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Servers1, callbackActionOnDirection)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityServers1);
+ params->param1 = 1;
+ }
+ }
+
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(5, Servers1, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Servers1, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Servers1, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction122358304);
+ setCallback(2);
+ setup_draw("008C");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction122288808);
+ setCallback(2);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(1, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Servers1, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityServers1, kAction223002560, 0);
+ getSavePoints()->addData(kEntityServers1, kAction302996448, 2);
+ getSavePoints()->addData(kEntityServers1, kAction269485588, 3);
+ getSavePoints()->addData(kEntityServers1, kAction326144276, 4);
+ getSavePoints()->addData(kEntityServers1, kAction302203328, 5);
+ getSavePoints()->addData(kEntityServers1, kAction189688608, 6);
+ getSavePoints()->addData(kEntityServers1, kAction236237423, 7);
+ getSavePoints()->addData(kEntityServers1, kAction219377792, 8);
+ getSavePoints()->addData(kEntityServers1, kAction256200848, 9);
+ getSavePoints()->addData(kEntityServers1, kAction291721418, 10);
+ getSavePoints()->addData(kEntityServers1, kAction258136010, 11);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Servers1, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMilos, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityServers1, "009B");
+
+ setCallback(2);
+ setup_playSound("WAT1001");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+
+ setCallback(3);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Servers1, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityServers1, "008C");
+
+ setCallback(2);
+ setup_playSound("MRB1077");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction168717392);
+
+ setCallback(3);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Servers1, function11)
+ serveTable(savepoint, "919", kEntityTables1, "005H", "005J", "921", &ENTITY_PARAM(0, 3), 63);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Servers1, function12)
+ serveTable(savepoint, "923", kEntityTables2, "009F", "009G", "926", &ENTITY_PARAM(0, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Servers1, function13)
+ serveTable(savepoint, "923", kEntityTables2, "009F", "009G", "926", &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Servers1, chapter1Handler)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(0, 1)) {
+ setCallback(1);
+ setup_function9();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(2);
+ setup_function10();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function11();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_function12();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(5);
+ setup_function13();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 5) {
+ getSavePoints()->push(kEntityServers1, kEntityPascale, kAction352768896);
+ setup_function15();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Servers1, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ break;
+
+ case kAction101632192:
+ setup_function16();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Servers1, function16)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityServers1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Servers1, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Servers1, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(1);
+ setup_function19();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(2);
+ setup_function20();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 8) || ENTITY_PARAM(0, 5)) {
+ setCallback(3);
+ setup_function21();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 4:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ break;
+ }
+ break;
+
+ case kAction101106391:
+ setCallback(4);
+ setup_draw("975");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Servers1, function19)
+ serveTable(savepoint, "969", kEntityTables1, "005H2", "018A", "971", &ENTITY_PARAM(0, 6), 63);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Servers1, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("973");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSavePoints()->push(kEntityServers1, kEntityIvo, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityServers1, "BLANK");
+ ENTITY_PARAM(0, 7) = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Servers1, function21)
+ serveTable(savepoint, "974", kEntityTables2, "009F2", "009G", "976", &ENTITY_PARAM(0, 8), 0, true, &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Servers1, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Servers1, chapter3Handler)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ return;
+
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(1);
+ setup_function24();
+ return;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(2);
+ setup_function7();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Servers1, function24)
+ serveSalon(savepoint, "927", "Ann3143A", kEntityAnna, "Ann31444", "112C", kAction122288808, "928", &ENTITY_PARAM(1, 1));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Servers1, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->clearSequences(kEntityServers1);
+
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Servers1, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 900)
+ ENTITY_PARAM(1, 5) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(2);
+ setup_function28();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(3);
+ setup_function29();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(4);
+ setup_function7();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 1;
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Servers1, function27)
+ serveSalon(savepoint, "929", "", kEntityAugust, "Aug4003", "122D", kAction134486752, "930", &ENTITY_PARAM(1, 3));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Servers1, function28)
+ serveSalon(savepoint, "931", "", kEntityAugust, "Aug4004", "122E", kAction125826561, "930", &ENTITY_PARAM(1, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Servers1, function29)
+ serveSalon(savepoint, "932", "", kEntityAnna, "Ann4151", "127D", kAction122288808, "930", &ENTITY_PARAM(1, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Servers1, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Servers1, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(32, Servers1)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Servers1::serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, Position position, bool shouldUpdatePosition, uint *parameter2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (shouldUpdatePosition) {
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ }
+
+ setCallback(1);
+ setup_draw(seq1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (position)
+ getEntities()->updatePositionEnter(kEntityServers1, kCarRestaurant, position);
+
+ getSavePoints()->push(kEntityServers1, entity, kAction136455232);
+
+ setCallback(2);
+ setup_callSavepoint(seq2, entity, kActionDrawTablesWithChairs, seq3);
+ break;
+
+ case 2:
+ if (position)
+ getEntities()->updatePositionExit(kEntityServers1, kCarRestaurant, position);
+
+ setCallback(3);
+ setup_draw(seq4);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers1);
+ *parameter = 0;
+
+ if (parameter2 != NULL)
+ *parameter2 = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Servers1::serveSalon(const SavePoint &savepoint, const char *seq1, const char *snd1, EntityIndex entity, const char *snd2, const char *seq2, ActionIndex action, const char *seq3, uint *parameter) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("816DD");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityServers1, seq1);
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityServers1);
+
+ if (!strcmp(snd1, ""))
+ getSound()->playSound(kEntityServers1, snd1);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, entity, kAction122358304);
+
+ getSound()->playSound(kEntityServers1, snd2);
+
+ setCallback(3);
+ setup_updatePosition(seq2, kCarRestaurant, 57);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityServers1, entity, action);
+
+ setCallback(4);
+ setup_draw(seq3);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityServers1, "816UD");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityServers1);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/servers1.h b/engines/lastexpress/entities/servers1.h
new file mode 100644
index 0000000000..14a6bf09a6
--- /dev/null
+++ b/engines/lastexpress/entities/servers1.h
@@ -0,0 +1,167 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SERVERS1_H
+#define LASTEXPRESS_SERVERS1_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Servers1 : public Entity {
+public:
+ Servers1(LastExpressEngine *engine);
+ ~Servers1() {}
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence, CarIndex car, Position position)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function24)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, Position position = 0, bool updatePosition = true, uint *parameter2 = NULL);
+ void serveSalon(const SavePoint &savepoint, const char *seq1, const char *snd1, EntityIndex entity, const char *snd2, const char *seq2, ActionIndex action, const char *seq3, uint *parameter);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SERVERS1_H
diff --git a/engines/lastexpress/entities/sophie.cpp b/engines/lastexpress/entities/sophie.cpp
new file mode 100644
index 0000000000..1b2d7b92ee
--- /dev/null
+++ b/engines/lastexpress/entities/sophie.cpp
@@ -0,0 +1,279 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/sophie.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+#define CHAPTER_IMPLEMENTATION() \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionNone: \
+ setup_chaptersHandler(); \
+ break; \
+ case kActionDefault: \
+ getEntities()->clearSequences(kEntitySophie); \
+ getData()->entityPosition = kPosition_4840; \
+ getData()->location = kLocationInsideCompartment; \
+ getData()->car = kCarRedSleeping; \
+ getData()->clothes = kClothesDefault; \
+ getData()->inventoryItem = kItemNone; \
+ break; \
+ }
+
+#define DEFAULT_ACTION_IMPLEMENTATION() \
+ if (savepoint.action == kActionDefault) { \
+ getData()->entityPosition = kPosition_4840; \
+ getData()->location = kLocationInsideCompartment; \
+ getData()->car = kCarRedSleeping; \
+ getEntities()->clearSequences(kEntitySophie); \
+ }
+
+Sophie::Sophie(LastExpressEngine *engine) : Entity(engine, kEntitySophie) {
+ ADD_CALLBACK_FUNCTION(Sophie, reset);
+ ADD_CALLBACK_FUNCTION(Sophie, updateEntity);
+ ADD_CALLBACK_FUNCTION(Sophie, chaptersHandler);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter1);
+ ADD_CALLBACK_FUNCTION(Sophie, function5);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter2);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter3);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter4);
+ ADD_CALLBACK_FUNCTION(Sophie, function9);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter5);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Sophie, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(2, Sophie, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ params->param3 = 0;
+
+ // Sophie
+ byte direction = getData()->direction;
+ EntityPosition position = getData()->entityPosition;
+ CarIndex car = getData()->car;
+
+ // Rebecca
+ EntityPosition rebecca_position = getEntityData(kEntityRebecca)->entityPosition;
+ CarIndex rebeccaCar = getEntityData(kEntityRebecca)->car;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntitySophie, kEntityRebecca, 500)
+ || (direction == kDirectionUp && car >= rebeccaCar && position > rebecca_position)
+ || (direction == kDirectionDown && car <= rebeccaCar && position < rebecca_position)) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntitySophie, (CarIndex)params->param1, (EntityPosition)params->param2);
+
+ break;
+ }
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntitySophie);
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntitySophie, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Sophie, chaptersHandler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityRebecca)->entityPosition;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntitySophie, "BLANK");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntitySophie, "BLANK");
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition - 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kAction136654208:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition + 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kAction259921280:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition + 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(3);
+ setup_updateEntity(kCarKronos, kPosition_9460);
+ break;
+
+ case kAction292775040:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Sophie, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Sophie, function5)
+ DEFAULT_ACTION_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Sophie, chapter2)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Sophie, chapter3)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Sophie, chapter4)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Sophie, function9)
+ DEFAULT_ACTION_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Sophie, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySophie);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Sophie, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(12, Sophie)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/sophie.h b/engines/lastexpress/entities/sophie.h
new file mode 100644
index 0000000000..24a70188da
--- /dev/null
+++ b/engines/lastexpress/entities/sophie.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SOPHIE_H
+#define LASTEXPRESS_SOPHIE_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Sophie : public Entity {
+public:
+ Sophie(LastExpressEngine *engine);
+ ~Sophie() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handle chapters events
+ */
+ DECLARE_FUNCTION(chaptersHandler)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function5)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SOPHIE_H
diff --git a/engines/lastexpress/entities/tables.cpp b/engines/lastexpress/entities/tables.cpp
new file mode 100644
index 0000000000..eca60a536b
--- /dev/null
+++ b/engines/lastexpress/entities/tables.cpp
@@ -0,0 +1,221 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/tables.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Tables::Tables(LastExpressEngine *engine, EntityIndex id) : Entity(engine, id) {
+ _id = id;
+
+ ADD_CALLBACK_FUNCTION(Tables, chapter1);
+ ADD_CALLBACK_FUNCTION(Tables, chapter2);
+ ADD_CALLBACK_FUNCTION(Tables, chapter3);
+ ADD_CALLBACK_FUNCTION(Tables, chapter4);
+ ADD_CALLBACK_FUNCTION(Tables, chapter5);
+ ADD_CALLBACK_FUNCTION(Tables, draw);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Tables, chapter1)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Tables, chapter2)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Tables, chapter3)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Tables, chapter4)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Tables, chapter5)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2 && getSound()->isBuffered(kEntityTables2))
+ getSound()->processEntry(kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Tables, draw)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Only applicable to Tables2 entity
+ if (_id != kEntityTables2)
+ break;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ if (getState()->time > kTime1165500 && !params->param1) {
+ params->param1 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ case kChapter3:
+ if (getState()->time > kTime2052000 && !params->param2) {
+ params->param2 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ case kChapter4:
+ if (getState()->time > kTime2488500 && !params->param3) {
+ params->param3 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ }
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ switch(_id) {
+ default:
+ break;
+
+ case kEntityTables0:
+ getData()->entityPosition = kPosition_3970;
+ getEntities()->drawSequenceLeft(_id, "001P");
+ break;
+
+ case kEntityTables1:
+ getData()->entityPosition = kPosition_3970;
+ getEntities()->drawSequenceLeft(_id, "005J");
+ break;
+
+ case kEntityTables2:
+ getData()->entityPosition = kPosition_4690;
+ getEntities()->drawSequenceLeft(_id, "009G");
+ break;
+
+ case kEntityTables3:
+ getData()->entityPosition = kPosition_4690;
+ getEntities()->drawSequenceLeft(_id, "010M");
+ break;
+
+ case kEntityTables4:
+ getData()->entityPosition = kPosition_5420;
+ getEntities()->drawSequenceLeft(_id, "014F");
+ break;
+
+ case kEntityTables5:
+ getData()->entityPosition = kPosition_5420;
+ getEntities()->drawSequenceLeft(_id, "024D");
+ break;
+ }
+
+ break;
+
+ case kActionDrawTablesWithChairs:
+ if (!strcmp(savepoint.param.charValue, "")) {
+ getEntities()->drawSequenceLeft(_id, savepoint.param.charValue);
+ } else {
+ switch(_id) {
+ default:
+ break;
+
+ case kEntityTables0:
+ getEntities()->drawSequenceLeft(_id, "001P");
+ break;
+
+ case kEntityTables1:
+ getEntities()->drawSequenceLeft(_id, "005J");
+ break;
+
+ case kEntityTables2:
+ getEntities()->drawSequenceLeft(_id, "009G");
+ break;
+
+ case kEntityTables3:
+ getEntities()->drawSequenceLeft(_id, "010M");
+ break;
+
+ case kEntityTables4:
+ getEntities()->drawSequenceLeft(_id, "014F");
+ break;
+
+ case kEntityTables5:
+ getEntities()->drawSequenceLeft(_id, "024D");
+ break;
+ }
+ }
+ break;
+
+ case kAction136455232:
+ getEntities()->drawSequenceLeft(_id, "BLANK");
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/tables.h b/engines/lastexpress/entities/tables.h
new file mode 100644
index 0000000000..e5fe28128b
--- /dev/null
+++ b/engines/lastexpress/entities/tables.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_TABLES_H
+#define LASTEXPRESS_TABLES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Tables : public Entity {
+public:
+ Tables(LastExpressEngine *engine, EntityIndex id);
+ ~Tables() {}
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Draws tables
+ */
+ DECLARE_FUNCTION(draw)
+
+private:
+ EntityIndex _id; ///< Table entity id
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TABLES_H
diff --git a/engines/lastexpress/entities/tatiana.cpp b/engines/lastexpress/entities/tatiana.cpp
new file mode 100644
index 0000000000..8e6be41ad4
--- /dev/null
+++ b/engines/lastexpress/entities/tatiana.cpp
@@ -0,0 +1,2272 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/tatiana.h"
+
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Tatiana::Tatiana(LastExpressEngine *engine) : Entity(engine, kEntityTatiana) {
+ ADD_CALLBACK_FUNCTION(Tatiana, reset);
+ ADD_CALLBACK_FUNCTION(Tatiana, playSound);
+ ADD_CALLBACK_FUNCTION(Tatiana, draw);
+ ADD_CALLBACK_FUNCTION(Tatiana, updatePosition);
+ ADD_CALLBACK_FUNCTION(Tatiana, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Tatiana, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Tatiana, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Tatiana, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Tatiana, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Tatiana, savegame);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateEntity);
+ ADD_CALLBACK_FUNCTION(Tatiana, function14);
+ ADD_CALLBACK_FUNCTION(Tatiana, function15);
+ ADD_CALLBACK_FUNCTION(Tatiana, function16);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter1);
+ ADD_CALLBACK_FUNCTION(Tatiana, function18);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function20);
+ ADD_CALLBACK_FUNCTION(Tatiana, function21);
+ ADD_CALLBACK_FUNCTION(Tatiana, function22);
+ ADD_CALLBACK_FUNCTION(Tatiana, function23);
+ ADD_CALLBACK_FUNCTION(Tatiana, function24);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter2);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function27);
+ ADD_CALLBACK_FUNCTION(Tatiana, function28);
+ ADD_CALLBACK_FUNCTION(Tatiana, function29);
+ ADD_CALLBACK_FUNCTION(Tatiana, function30);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter3);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function33);
+ ADD_CALLBACK_FUNCTION(Tatiana, function34);
+ ADD_CALLBACK_FUNCTION(Tatiana, function35);
+ ADD_CALLBACK_FUNCTION(Tatiana, function36);
+ ADD_CALLBACK_FUNCTION(Tatiana, function37);
+ ADD_CALLBACK_FUNCTION(Tatiana, function38);
+ ADD_CALLBACK_FUNCTION(Tatiana, function39);
+ ADD_CALLBACK_FUNCTION(Tatiana, function40);
+ ADD_CALLBACK_FUNCTION(Tatiana, function41);
+ ADD_CALLBACK_FUNCTION(Tatiana, function42);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter4);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function45);
+ ADD_CALLBACK_FUNCTION(Tatiana, function46);
+ ADD_CALLBACK_FUNCTION(Tatiana, function47);
+ ADD_CALLBACK_FUNCTION(Tatiana, function48);
+ ADD_CALLBACK_FUNCTION(Tatiana, function49);
+ ADD_CALLBACK_FUNCTION(Tatiana, function50);
+ ADD_CALLBACK_FUNCTION(Tatiana, function51);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter5);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function54);
+ ADD_CALLBACK_FUNCTION(Tatiana, function55);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Tatiana, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Tatiana, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Tatiana, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(4, Tatiana, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, Tatiana, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Tatiana, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_7500, kPosition_7850, kCarRedSleeping, kObjectCompartmentB);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(7, Tatiana, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Tatiana, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(9, Tatiana, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(10, Tatiana, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Tatiana, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(12, Tatiana, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Tatiana, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventTatianaAskMatchSpeakRussian) || getEvent(kEventTatianaAskMatch) || getEvent(kEventVassiliSeizure)) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1010" : "CAT1010A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Tatiana, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction326348944);
+ getEntities()->drawSequenceLeft(kEntityTatiana, getProgress().chapter == kChapter1 ? "603Fb" : "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction69239528:
+ setCallback(getProgress().chapter == kChapter1 ? 1 : 2);
+ setup_enterExitCompartment2(getProgress().chapter == kChapter1 ? "603Db" : "673Db", kObjectCompartmentB);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Tatiana, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getProgress().chapter == kChapter1 ? 1 : 2);
+ setup_enterExitCompartment2(getProgress().chapter == kChapter1 ? "603Bb" : "673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction292048641);
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, getProgress().chapter == kChapter1 ? "603Fb" : "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ }
+ break;
+
+ case kAction69239528:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Tatiana, function16, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param4) {
+ params->param4 = 1;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param2) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.param.intValue == 49) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1512" : getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ } else {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "TAT1133A" : "TAT1133B");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Tatiana, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityTatiana, kAction191198209, 0);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_5419;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Tatiana, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+
+ if (getState()->time > kTime1143000 && !params->param2) {
+ params->param2 = 1;
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 4500)
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (getData()->entityPosition <= kPosition_2330) {
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction157159392);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction188784532);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ } else {
+ getEntities()->clearSequences(kEntityTatiana);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ getEntities()->updateFrame(kEntityTatiana);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Tatiana, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getSound()->isBuffered(kEntityTatiana) || !params->param4 || params->param3 == 2 || getSound()->isBuffered("TAT1066"))
+ goto label_tatiana_chapter1_2;
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 450)
+ getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A");
+ getProgress().field_64 = 1;
+ params->param3++;
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 71)) {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75)
+ getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A");
+ getProgress().field_64 = 1;
+ params->param3++;
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_tatiana_chapter1_2:
+ TIME_CHECK_SAVEPOINT(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762);
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ } else {
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityTables4, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityTatiana, "014A");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 67) ? 1 : 0;
+ params->param4 = getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71);
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "014A");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "BLANK");
+ break;
+
+ case kAction124973510:
+ setup_function20();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Tatiana, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityTatiana, kEntityAugust, kAction223183000);
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarRestaurant, 67);
+ getSound()->playSound(kEntityTatiana, "TAT1070");
+
+ setCallback(2);
+ setup_callSavepoint("014C", kEntityTables4, kActionDrawTablesWithChairs, "014D");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityTatiana, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityTatiana, kEntityServers0, kAction188893625);
+
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityTatiana, kEntityAugust, kAction268620864);
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Tatiana, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes1;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8513);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->clothes = kClothesDefault;
+
+ getSound()->playSound(kEntityTatiana, "TAT1071");
+ getEntities()->drawSequenceRight(kEntityTatiana, "604Aa");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentA);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentA, true);
+ }
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentA);
+
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityTatiana);
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction135854208);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ // Fallback to next case
+
+ case 3:
+ if (getSound()->isBuffered(kEntityTatiana)) {
+ setCallback(3);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(4);
+ setup_playSound("TAT1071A");
+ }
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_7500;
+
+ getSavePoints()->push(kEntityTatiana, kEntityVassili, kAction168459827);
+
+ setCallback(5);
+ setup_function16(kTime1156500);
+ break;
+
+ case 5:
+ case 6:
+ if (getProgress().field_14 == 29) {
+ setCallback(6);
+ setup_function16((uint)getState()->time + 900);
+ } else {
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_function22();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Tatiana, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 == kTimeInvalid || getState()->time <= kTime1179000)
+ goto label_update;
+
+ UPDATE_PARAM_PROC_TIME(kTime1233000, ((!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch)) || getEntities()->isInGreenCarEntrance(kEntityPlayer)), params->param1, 0)
+label_update:
+ if (!getEvent(kEventTatianaAskMatchSpeakRussian)
+ && !getEvent(kEventTatianaAskMatch)
+ && getInventory()->hasItem(kItemMatchBox)
+ && getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getObjects()->update(kObject25, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectTrainTimeTable, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward);
+ }
+ UPDATE_PARAM_PROC_END
+
+ params->param1 = kTimeInvalid;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ setup_function23();
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaGivePoem);
+ break;
+
+ case kActionOpenDoor:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaAskMatchSpeakRussian);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityVassili, kAction122732000);
+
+ setCallback(1);
+ setup_function15();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, "306B");
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarGreenSleeping, 71);
+ break;
+
+ case 3:
+ getAction()->playAnimation(getEvent(kEventAlexeiSalonVassili) ? kEventTatianaAskMatchSpeakRussian : kEventTatianaAskMatch);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 62);
+ getData()->inventoryItem = kItemParchemin;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventTatianaGivePoem);
+ getInventory()->removeItem(kItemParchemin);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Tatiana, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Tatiana, function24)
+ if (savepoint.action == kActionDefault) {
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 71);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Tatiana, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_5420;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Tatiana, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1800000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function28();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setup_function28();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "024A");
+ getSavePoints()->push(kEntityTatiana, kEntityTables5, kAction136455232);
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 64) || getEntities()->isPlayerPosition(kCarRestaurant, 65)) {
+ getData()->inventoryItem = kItemNone;
+ setup_function27();
+ }
+ break;
+
+ case kAction290869168:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Tatiana, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getEvent(kEventTatianaGivePoem) ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, getEvent(kEventTatianaGivePoem) ? kEventTatianaBreakfastAlexei : kEventTatianaBreakfast);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function30);
+ getAction()->playAnimation(kEventTatianaBreakfastAlexei);
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->setLocationAndProcess(kItem11, kObjectLocation1);
+ setup_function28();
+ break;
+
+ case 2:
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function30);
+ getAction()->playAnimation(kEventTatianaBreakfast);
+ if (getInventory()->hasItem(kItemParchemin)) {
+ getAction()->playAnimation(kEventTatianaBreakfastGivePoem);
+ getInventory()->removeItem(kItemParchemin);
+ } else {
+ getAction()->playAnimation(kEventTatianaAlexei);
+ }
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Tatiana, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityTatiana, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction236053296, (getEvent(kEventTatianaBreakfastAlexei) || getEvent(kEventTatianaBreakfast)) ? 69 : 0);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function29();
+ break;
+
+ case kAction123857088:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "018G");
+
+ setCallback(1);
+ setup_updateFromTime(1800);
+ break;
+
+ case kAction156444784:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Tatiana, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarRestaurant, 63);
+
+ setCallback(2);
+ setup_callSavepoint("018H", kEntityTables1, kActionDrawTablesWithChairs, "018A");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityTatiana, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityTatiana, kEntityServers1, kAction302203328);
+ getEntities()->drawSequenceRight(kEntityTatiana, "805DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityTatiana);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Tatiana, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(kTimeEnd);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Tatiana, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_1750;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ // Update inventory
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+
+ if (getEvent(kEventTatianaBreakfastGivePoem) || (getEvent(kEventTatianaGivePoem) && !getEvent(kEventTatianaBreakfastAlexei)))
+ getInventory()->get(kItemParchemin)->location = kObjectLocation2;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Tatiana, chapter3Handler)
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+ EntityData::EntityParametersSIII *parameters1 = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!parameters->param2 && !parameters->param5) {
+ parameters->param1 -= getState()->timeDelta;
+
+ if (getState()->timeDelta > parameters->param1) {
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, (char *)&parameters1->seq);
+ getSound()->playSound(kEntityTatiana, (char *)&parameters->seq);
+
+ if (parameters->param3 == 4 && getEntities()->isInSalon(kEntityPlayer))
+ getProgress().field_90 = 1;
+
+ parameters->param2 = 1;
+ }
+ }
+
+ if (parameters->param4 && parameters->param5) {
+ UPDATE_PARAM_CHECK(parameters->param4, getState()->time, 6300)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("110E", kCarRestaurant, 52);
+ }
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ parameters->param2 = 0;
+ ++parameters->param3;
+
+ switch (parameters->param3) {
+ default:
+ parameters->param5 = 1;
+ break;
+
+ case 1:
+ parameters->param1 = 900;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110A");
+ strcpy((char *)&parameters->seq, "Tat3160B");
+ strcpy((char *)&parameters1->seq, "110A");
+ break;
+
+ case 2:
+ parameters->param1 = 9000;
+ strcpy((char *)&parameters->seq, "Tat3160C");
+ strcpy((char *)&parameters1->seq, "110C");
+ break;
+
+ case 3:
+ parameters->param1 = 13500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160D");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 4:
+ parameters->param1 = 9000;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160E");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 5:
+ parameters->param1 = 4500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160G");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 6:
+ parameters->param1 = 4500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160B");
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction122358304);
+ getSavePoints()->push(kEntityTatiana, kEntityKronos, kAction157159392);
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110C");
+ getSound()->playSound(kEntityTatiana, "Tat3160A");
+
+ parameters->param2 = 1;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction122288808);
+ setup_function33();
+ }
+ break;
+
+ case kAction101169422:
+ parameters->param4 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Tatiana, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+ setCallback(1);
+ setup_updateFromTime(75);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function14();
+ break;
+
+ case 3:
+ setup_function34();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Tatiana, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2097000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation1;
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850))
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ setup_function15();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 3:
+ setup_function35();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Tatiana, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1
+ && getInventory()->hasItem(kItemFirebird)
+ && getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && (getState()->time < kTime2133000 || getProgress().field_40)) {
+ setCallback(1);
+ setup_function41();
+ break;
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2133000) {
+ if (getData()->car >= kCarRedSleeping || (getData()->car == kCarGreenSleeping && getData()->entityPosition > kPosition_5790))
+ setup_function36();
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ params->param1 = 1;
+ goto label_callback_1;
+ }
+ break;
+
+ case kAction191668032:
+ setup_function36();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Tatiana, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850) || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ setCallback(2);
+ setup_function14();
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ break;
+
+ case 2:
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Tatiana, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getInventory()->get(kItemFirebird)->location != kObjectLocation1 && getInventory()->get(kItemFirebird)->location != kObjectLocation2) {
+ if(!params->param3)
+ params->param3 = (uint)getState()->time + 900;
+
+ if (params->param4 != kTimeInvalid && params->param3 < getState()->time) {
+ UPDATE_PARAM_PROC_TIME(kTime2227500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param4, 450)
+ getProgress().field_5C = 1;
+ if (getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070)) {
+ setup_function38();
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.param.intValue == 49) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1512" : getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ if (savepoint.param.intValue == 49) {
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "TAT1133A" : "TAT1133B");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Tatiana, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 450);
+
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true);
+
+ setCallback(4);
+ setup_function42(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes3;
+
+ setCallback(1);
+ setup_enterExitCompartment("673Jb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_function42(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Gf");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentF, true);
+
+ setCallback(3);
+ setup_playSound("Tat3164");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityTatiana, kEntityAnna, kAction236241630);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function39();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true);
+ getEntities()->clearSequences(kEntityTatiana);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(7);
+ setup_playSound("ANN3011");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(900);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("673Jf", kObjectCompartmentF);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_function42(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityTatiana, kEntityAnna, kAction236517970);
+
+ setCallback(11);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 11:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function39();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ setCallback(6);
+ setup_enterExitCompartment("673Df", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Tatiana, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntities()->isDistanceBetweenEntities(kEntityTatiana, kEntityPlayer, 1000)) {
+ params->param1 = 1;
+ getSound()->playSound(kEntityTatiana, "Tat3164"); // Tatiana weeping
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Tatiana, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)
+ || getData()->car != getEntityData(kEntityPlayer)->car
+ || getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMe:
+ if (getEvent(kEventTatianaAskMatchSpeakRussian) || getEvent(kEventTatianaAskMatch) || getEvent(kEventVassiliSeizure))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1001A" : "CAT1010");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Tatiana, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && !getEvent(kEventVassiliCompartmentStealEgg)
+ && (getState()->time <= kTime2133000 || getProgress().field_40)) {
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7500)) {
+
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction235061888);
+ getEntities()->clearSequences(kEntityTatiana);
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+
+ setCallback(4);
+ setup_updateFromTime(150);
+ }
+ } else {
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+
+ if (getState()->time < kTime2133000 || getProgress().field_40) {
+ setCallback(3);
+ setup_function40();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityTatiana);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function51);
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Tat3161B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction168316032);
+ params->param1 = 1;
+ break;
+
+ case 3:
+ case 6:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function40();
+ break;
+ }
+ break;
+
+ case kAction154071333:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(42, Tatiana, function42, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath || savepoint.action == kActionExcuseMe) {
+ getSound()->playSound(kEntityPlayer, "Tat3124", getSound()->getSoundFlag(kEntityTatiana));
+ return;
+ }
+
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Tatiana, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 1) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Tatiana, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2362500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Tatiana, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction123712592);
+ setup_function46();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Tatiana, function46)
+ // Expose parameters as IIIIIS and ignore the default exposed parameters
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!parameters->param2 && !parameters->param3) {
+ parameters->param1 -= getState()->timeDelta;
+
+ if (parameters->param1 < getState()->timeDelta) {
+ getSound()->playSound(kEntityTatiana, (char *)&parameters->seq);
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityTatiana, kEntityPlayer, 2000)) {
+ if (parameters->param4 == 4)
+ getProgress().field_8C = 1;
+ else if (parameters->param4 == 7)
+ getProgress().field_88 = 1;
+ }
+
+ parameters->param2 = 1;
+ }
+ }
+
+ if (CURRENT_PARAM(1, 1) == kTimeInvalid || getState()->time <= kTime2394000)
+ break;
+
+ if (getState()->time >= kTime2398500) {
+ CURRENT_PARAM(1, 1) = kTimeInvalid;
+ } else {
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) || !CURRENT_PARAM(1, 1))
+ CURRENT_PARAM(1, 1) = (uint)getState()->time;
+
+ if (CURRENT_PARAM(1, 1) >= getState()->time)
+ break;
+
+ CURRENT_PARAM(1, 1) = kTimeInvalid;
+ }
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction123536024);
+
+ setup_function47();
+ break;
+
+ case kActionEndSound:
+ parameters->param2 = 0;
+ ++parameters->param4;
+
+ switch(parameters->param4) {
+ default:
+ parameters->param1 = 162000;
+ break;
+
+ case 1:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165F");
+ break;
+
+ case 2:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165B");
+ break;
+
+ case 3:
+ parameters->param1 = 1800;
+ strcpy((char *)&parameters->seq, "Tat4165G");
+ break;
+
+ case 4:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165H");
+ break;
+
+ case 5:
+ parameters->param1 = 2700;
+ strcpy((char *)&parameters->seq, "Tat4165C");
+ break;
+
+ case 6:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165D");
+ break;
+
+ case 7:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165E");
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "306E");
+ parameters->param1 = 450;
+ strcpy((char *)&parameters->seq, "Tat4165A");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ parameters->param3 = 1;
+
+ if (parameters->param2) {
+ getSound()->removeFromQueue(kEntityTatiana);
+ getSavePoints()->call(kEntityTatiana, kEntityTatiana, kActionEndSound);
+ }
+ } else {
+ parameters->param3 = 0;
+ parameters->param5 = 0;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62) && !parameters->param5) {
+ setCallback(1);
+ setup_draw("306D");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityTatiana, "306E");
+ parameters->param5 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Tatiana, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setCallback(3);
+ setup_function16(kTime2407500);
+ break;
+
+ case 3:
+ case 4:
+ if (ENTITY_PARAM(0, 1) && getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) {
+ setup_function48();
+ } else {
+ setCallback(4);
+ setup_function16(900);
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Tatiana, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ if (!getEvent(kEventTatianaTylerCompartment) && getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) {
+ params->param1 = 1;
+ getProgress().field_E4 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityTatiana, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ }
+
+ if (!params->param1)
+ goto label_end;
+ }
+
+ if (!getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850)) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ }
+
+ if (!params->param1 || getSound()->isBuffered(kEntityTatiana))
+ goto label_end;
+
+ UPDATE_PARAM_GOTO(params->param2, getState()->timeTicks, 5 * (3 * rnd(5) + 30), label_end);
+
+ getSound()->playSound(kEntityTatiana, "LIB012", SoundManager::kFlagDefault);
+ params->param2 = 0;
+
+label_end:
+ if (getEvent(kEventTatianaTylerCompartment) || getState()->time > kTime2475000) {
+ if (params->param1)
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_E4 = 0;
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartment2, true);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ }
+ break;
+
+ case kActionOpenDoor:
+ params->param1 = 0;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaTylerCompartment);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartment2, true);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function49();
+ break;
+
+ case 5:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getAction()->playAnimation(kEventTatianaTylerCompartment);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 7:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartment2, true);
+ break;
+ }
+ break;
+
+ case kAction238790488:
+ params->param1 = 0;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartment2, true);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_9460;
+
+ setCallback(6);
+ setup_updateFromTime(1800);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Tatiana, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction169360385:
+ setup_function50();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Tatiana, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2520000 && !params->param1) {
+ params->param1 = 1;
+ setup_function51();
+ }
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityTatiana, "Tat4166");
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliDeadAlexei);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityTatiana, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentA, kEntityTatiana, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ if (!getSound()->isBuffered(kEntityTatiana))
+ getSound()->playSound(kEntityTatiana, "Tat4166");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getSound()->isBuffered("MUS013"))
+ getSound()->processEntry("MUS013");
+
+ getAction()->playAnimation(kEventVassiliDeadAlexei);
+ getSavePoints()->push(kEntityTatiana, kEntityAbbot, kAction104060776);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 38);
+
+ setup_function51();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Tatiana, function51)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Tatiana, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Tatiana, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function54();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Tatiana, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ switch (params->param1) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityTatiana, "Tat5167A");
+ params->param2 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTatiana, "Tat5167B");
+ params->param2 = 1;
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTatiana, "Tat5167C");
+ params->param2 = 1;
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTatiana, "Tat5167D");
+ params->param2 = 1;
+ break;
+ }
+ }
+
+ if (params->param1 > 3) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 225);
+
+ params->param1 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaVassiliTalk);
+ break;
+
+ case kActionEndSound:
+ ++params->param1;
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "033A");
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ if (getSound()->isBuffered(kEntityTatiana))
+ getSound()->processEntry(kEntityTatiana);
+
+ getAction()->playAnimation(isNight() ? kEventTatianaVassiliTalkNight : kEventTatianaVassiliTalk);
+ getScenes()->processScene();
+
+ params->param1 = 4;
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction203078272:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "033E");
+ break;
+
+ case kAction236060709:
+ getData()->inventoryItem = kItemNone;
+ setup_function55();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Tatiana, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+ // fall back to next action
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 72))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 86);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/tatiana.h b/engines/lastexpress/entities/tatiana.h
new file mode 100644
index 0000000000..171f7d62d3
--- /dev/null
+++ b/engines/lastexpress/entities/tatiana.h
@@ -0,0 +1,235 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_TATIANA_H
+#define LASTEXPRESS_TATIANA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Tatiana : public Entity {
+public:
+ Tatiana(LastExpressEngine *engine);
+ ~Tatiana() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION_1(function16, uint32)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * ???
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function42, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TATIANA_H
diff --git a/engines/lastexpress/entities/train.cpp b/engines/lastexpress/entities/train.cpp
new file mode 100644
index 0000000000..b3aa6e9a66
--- /dev/null
+++ b/engines/lastexpress/entities/train.cpp
@@ -0,0 +1,575 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/train.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Train::Train(LastExpressEngine *engine) : Entity(engine, kEntityTrain) {
+ ADD_CALLBACK_FUNCTION(Train, savegame);
+ ADD_CALLBACK_FUNCTION(Train, chapter1);
+ ADD_CALLBACK_FUNCTION(Train, chapter2);
+ ADD_CALLBACK_FUNCTION(Train, chapter3);
+ ADD_CALLBACK_FUNCTION(Train, chapter4);
+ ADD_CALLBACK_FUNCTION(Train, chapter5);
+ ADD_CALLBACK_FUNCTION(Train, harem);
+ ADD_CALLBACK_FUNCTION(Train, process);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(1, Train, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Train, chapter1)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Train, chapter2)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Train, chapter3)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Train, chapter4)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Train, chapter5)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Train, harem, ObjectIndex, uint32)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ switch (params->param1) {
+ default:
+ error("Train::harem: Invalid value for parameter 1: %d", params->param1);
+ break;
+
+ case kObjectCompartment5:
+ params->param3 = kPosition_4840;
+ break;
+
+ case kObjectCompartment6:
+ params->param3 = kPosition_4070;
+ break;
+
+ case kObjectCompartment7:
+ params->param3 = kPosition_3050;
+ break;
+
+ case kObjectCompartment8:
+ params->param3 = kPosition_2740;
+ break;
+ }
+
+ params->param4 = getEntities()->isInsideCompartment(kEntityAlouan, kCarGreenSleeping, (EntityPosition)params->param3);
+ params->param5 = (ENTITY_PARAM(0, 7) - params->param3) < 1 ? true : false;
+ params->param6 = getEntities()->isInsideCompartment(kEntityYasmin, kCarGreenSleeping, (EntityPosition)params->param3);
+ params->param7 = getEntities()->isInsideCompartment(kEntityHadija, kCarGreenSleeping, (EntityPosition)params->param3);
+
+ getObjects()->update((ObjectIndex)params->param1, kEntityTrain, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ // Knock / closed door sound
+ getSound()->playSound(kEntityTables5, (params->param2 == 8) ? "LIB012" : "LIB013", SoundManager::kFlagDefault);
+
+ if (params->param4 && params->param5) {
+
+ ENTITY_PARAM(0, 5)++;
+
+ switch (ENTITY_PARAM(0, 5)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ getSound()->playSound(kEntityTables5, "Har1016", SoundManager::kFlagDefault, 150);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTables5, "Har1015A", SoundManager::kFlagDefault, 15);
+ getSound()->playSound(kEntityTables5, "Har1015", SoundManager::kFlagDefault, 150);
+ break;
+ }
+
+ // Update progress
+ getProgress().field_DC = 1;
+ getProgress().field_E0 = 1;
+
+ handleCompartmentAction();
+
+ // Done with it!
+ return;
+ }
+
+ if (params->param6 && params->param7) {
+
+ ENTITY_PARAM(0, 6)++;
+
+ switch(ENTITY_PARAM(0, 6)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTables5, "Har1013A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ handleCompartmentAction();
+ return;
+ }
+
+ if (!params->param5) {
+
+ if (params->param6) {
+ ENTITY_PARAM(0, 3)++;
+
+ switch(ENTITY_PARAM(0, 3)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1012", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1012A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ handleCompartmentAction();
+ return;
+ } else {
+
+ if (params->param4) {
+ ENTITY_PARAM(0, 1)++;
+
+ if (ENTITY_PARAM(0, 1) <= 1)
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ else
+ params->param8 = 1;
+
+ getProgress().field_DC = 1;
+
+ handleCompartmentAction();
+ return;
+ }
+
+ if (params->param7) {
+ ENTITY_PARAM(0, 4)++;
+
+ if (ENTITY_PARAM(0, 4) <= 1) {
+ getSound()->playSound(kEntityTables5, "Har1011", SoundManager::kFlagDefault, 15);
+ handleCompartmentAction();
+ return;
+ }
+ }
+ }
+
+ params->param8 = 1;
+ handleCompartmentAction();
+ return;
+ }
+
+ ENTITY_PARAM(0, 2) += 1;
+
+ switch (ENTITY_PARAM(0, 2)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ getProgress().field_E0 = 1;
+
+ handleCompartmentAction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Train, process)
+ EntityData::EntityParametersIIIS *params1 = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Play smoke animation
+ if ((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && params->param4 && !params->param5) {
+
+ params->param4 -= 1;
+
+ if (!params->param4 && getProgress().jacket == kJacketGreen) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+ params->param5 = 1;
+ getScenes()->processScene();
+ }
+ }
+
+ if (params->param6) {
+ UPDATE_PARAM_GOTO(params1->param7, getState()->time, 900, label_process);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 58);
+ }
+
+ params1->param7 = 0;
+
+label_process:
+ if (params->param7) {
+ if (!params1->param8) {
+ params1->param8 = (uint)(getState()->time + 4500);
+
+ if (!params1->param8)
+ params->param7 = 0;
+ }
+
+ if (params1->param8 && params1->param8 < getState()->time) {
+ params->param7 = 0;
+ params1->param8 = 0;
+ }
+ }
+
+ // Update object
+ if (ENTITY_PARAM(0, 8) && !getSound()->isBuffered(kEntityTables5)) {
+ getObjects()->update((ObjectIndex)ENTITY_PARAM(0, 8), getObjects()->get((ObjectIndex)ENTITY_PARAM(0, 8)).entity, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ ENTITY_PARAM(0, 8) = 0;
+ }
+
+ // Play clock sound
+ if (params->param6 && !getSound()->isBuffered("ZFX1001", true))
+ getSound()->playSound(kEntityPlayer, "ZFX1001");
+
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor: {
+ // Handle opening harem compartments
+ ObjectIndex compartment = (ObjectIndex)savepoint.param.intValue;
+ if (compartment == kObjectCompartment5 || compartment == kObjectCompartment6 || compartment == kObjectCompartment7 || compartment == kObjectCompartment8) {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_harem(compartment, savepoint.action);
+ }
+ break;
+ }
+
+ case kActionDefault:
+ params->param3 = 1;
+ if (getProgress().chapter < kChapter5) {
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+ getData()->entityPosition = kPosition_30000;
+ break;
+
+ case kActionDrawScene:
+ getData()->car = getEntityData(kEntityPlayer)->car;
+
+ // Play clock sound
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 81)) {
+ params->param6 = 1;
+ if (!getSound()->isBuffered("ZFX1001"))
+ getSound()->playSound(kEntityPlayer, "ZFX1001");
+ } else {
+ params->param6 = 0;
+ if (getSound()->isBuffered("ZFX1001", true))
+ getSound()->removeFromQueue("ZFX1001");
+ }
+
+ // Draw moving background behind windows
+ if (params->param3) {
+ if (getEntityData(kEntityPlayer)->car != (CarIndex)params->param1 || isNight() != (bool)(params->param2)) {
+ switch (getEntityData(kEntityPlayer)->car) {
+ default:
+ getEntities()->clearSequences(kEntityTrain);
+ break;
+
+ case kCarBaggageRear:
+ case kCarBaggage:
+ if (getProgress().isNightTime)
+ getEntities()->drawSequenceLeft(kEntityTrain, "B1WNM");
+ else
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "B1WNN" : "B1WND");
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ if (getProgress().isNightTime)
+ getEntities()->drawSequenceLeft(kEntityTrain, "S1WNM");
+ else
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "S1WNN" : "S1WND");
+ break;
+
+ case kCarRestaurant:
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "RCWNN" : "RCWND");
+ break;
+ }
+
+ // Set parameters so we do not get called twice
+ params->param1 = getEntityData(kEntityPlayer)->car;
+ params->param2 = isNight();
+ }
+ }
+
+ if (!params->param5) {
+ params->param4 = 2700; // this is the sound file name
+ params->param5 = 0;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 22)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+ }
+
+ resetParam8();
+ break;
+
+
+ case kActionCallback: {
+ int action = getCallback();
+ switch(action) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getAction()->playAnimation(action == 1 ? kEventCoudertBloodJacket : kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ resetParam8();
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventLocomotiveConductorsDiscovered);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice2, true);
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventCathBreakCeiling);
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getScenes()->processScene();
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventCathJumpDownCeiling);
+ getScenes()->loadSceneFromPosition(kCarKronos, 89);
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventCloseMatchbox);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+ }
+ break;
+ }
+
+ case kAction191070912:
+ ENTITY_PARAM(0, 7) = savepoint.param.intValue;
+ break;
+
+ case kActionTrainStopRunning:
+ params->param3 = 0;
+ getEntities()->clearSequences(kEntityTrain);
+ break;
+
+ case kActionCatchBeetle:
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventCloseMatchbox);
+ break;
+
+ case kAction203339360:
+ if (params->param7) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveConductorsDiscovered);
+ } else {
+ params->param7 = 1;
+ getAction()->playAnimation(kEventLocomotiveConductorsLook);
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 2);
+ }
+ break;
+
+ case kActionTrainStartRunning:
+ if (!params->param3) {
+ params->param1 = 0;
+ params->param3 = 1;
+ getSavePoints()->push(kEntityTrain, kEntityTrain, kActionDrawScene);
+ }
+ break;
+
+ case kAction203863200:
+ if (!strcmp(savepoint.param.charValue, "")) {
+ params->param8 = 1;
+ strcpy((char *)&params1->seq, savepoint.param.charValue); // this is the sound file name
+ }
+ break;
+
+ case kAction222746496:
+ switch(savepoint.param.intValue) {
+ default:
+ break;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartmentA) ? kPosition_8200 : kPosition_7500;
+ params1->param3 = kPosition_7850;
+ break;
+
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment3 || savepoint.param.intValue == kObjectCompartmentC) ? kPosition_6470 : kPosition_5790;
+ params1->param3 = kPosition_6130;
+ break;
+
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment5 || savepoint.param.intValue == kObjectCompartmentE) ? kPosition_4840 : kPosition_4070;
+ params1->param3 = kPosition_4455;
+ break;
+
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ case kObjectCompartmentG:
+ case kObjectCompartmentH:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment7 || savepoint.param.intValue == kObjectCompartmentG) ? kPosition_3050 : kPosition_2740;
+ params1->param3 = kPositionNone;
+ break;
+ }
+ break;
+
+ case kActionBreakCeiling:
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventCathBreakCeiling);
+ break;
+
+ case kActionJumpDownCeiling:
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventCathJumpDownCeiling);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Train::handleCompartmentAction() {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ if (params->param8)
+ getSavePoints()->push(kEntityTrain, kEntityMahmud, kAction290410610, params->param1);
+
+ getAction()->handleOtherCompartment((ObjectIndex)params->param1, false, !params->param8);
+
+ ENTITY_PARAM(0, 8) = params->param1;
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Train::resetParam8() {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+ EntityData::EntityParametersIIIS *params1 = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters(1);
+
+ if (params->param8
+ && !getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params1->param1, (EntityPosition)params1->param2)
+ && !getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params1->param1, (EntityPosition)params1->param3)) {
+
+ if (getSound()->isBuffered((const char *)&params1->seq))
+ getSound()->processEntry((const char *)&params1->seq);
+
+ params->param8 = 0;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/train.h b/engines/lastexpress/entities/train.h
new file mode 100644
index 0000000000..95cb0f28bd
--- /dev/null
+++ b/engines/lastexpress/entities/train.h
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_TRAIN_H
+#define LASTEXPRESS_TRAIN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Train : public Entity {
+public:
+ Train(LastExpressEngine *engine);
+ ~Train() {}
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Harem events
+ *
+ * @param compartment The compartment to handle
+ * @param counter ??? (checked to decide which sound to make when knocking)
+ */
+ DECLARE_FUNCTION_2(harem, ObjectIndex compartment, uint32 counter)
+
+ /**
+ * Handles Train events
+ */
+ DECLARE_FUNCTION(process)
+
+private:
+ // Helper methods
+ void resetParam8();
+ void handleCompartmentAction();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TRAIN_H
diff --git a/engines/lastexpress/entities/vassili.cpp b/engines/lastexpress/entities/vassili.cpp
new file mode 100644
index 0000000000..64327c2534
--- /dev/null
+++ b/engines/lastexpress/entities/vassili.cpp
@@ -0,0 +1,589 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/vassili.h"
+
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Vassili::Vassili(LastExpressEngine *engine) : Entity(engine, kEntityVassili) {
+ ADD_CALLBACK_FUNCTION(Vassili, reset);
+ ADD_CALLBACK_FUNCTION(Vassili, draw);
+ ADD_CALLBACK_FUNCTION(Vassili, savegame);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter1);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Vassili, function6);
+ ADD_CALLBACK_FUNCTION(Vassili, function7);
+ ADD_CALLBACK_FUNCTION(Vassili, function8);
+ ADD_CALLBACK_FUNCTION(Vassili, function9);
+ ADD_CALLBACK_FUNCTION(Vassili, seizure);
+ ADD_CALLBACK_FUNCTION(Vassili, drawInBed);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter2);
+ ADD_CALLBACK_FUNCTION(Vassili, sleeping);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter3);
+ ADD_CALLBACK_FUNCTION(Vassili, stealEgg);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter4);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Vassili, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Vassili, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Vassili, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Vassili, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject40, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Vassili, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityTatiana)->entityPosition;
+ getData()->location = getEntityData(kEntityTatiana)->location;
+ } else {
+ if (params->param3 && params->param3 >= getState()->time) {
+ break;
+ }else {
+ params->param3 = (uint)getState()->time + 450;
+ if (params->param3 == 0)
+ break;
+ }
+
+ if (!params->param2 && getObjects()->get(kObjectCompartmentA).location == kObjectLocation1) {
+ params->param2 = 1;
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ }
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ break;
+
+ case kAction122732000:
+ setup_function6();
+ break;
+
+ case kAction168459827:
+ params->param1 = 0;
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Vassili, function6)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM_GOTO(params->param3, getState()->timeTicks, params->param1, label_function7);
+
+ setCallback(1);
+ setup_draw("303B");
+ break;
+ }
+
+ params->param3 = 0;
+
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+
+label_function7:
+ if (params->param4 != kTimeInvalid && getState()->time > kTime1489500) {
+
+ if (getState()->time <= kTime1503000) {
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200) || !params->param4) {
+
+ params->param4 = (uint)getState()->time;
+ if (!params->param4) {
+ setup_function7();
+ break;
+ }
+ }
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+ setup_function7();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 5 * (3 * rnd(25) + 15);
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+
+ // Shared part with kActionNone
+ goto label_function7;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Vassili, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid && getState()->time > kTime1503000) {
+
+ if (getState()->time <= kTime1512000) {
+ if (getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param1) {
+ params->param1 = (uint)getState()->time + 150;
+ if (params->param1) {
+ setup_function8();
+ break;
+ }
+ }
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+ setup_function8();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityVassili);
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200))
+ getScenes()->loadSceneFromObject(kObjectCompartmentA);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction339669520:
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Vassili, function8)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setup_function9();
+ break;
+
+ case kActionDefault:
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, (getEntityData(kEntityPlayer)->car <= kCarRedSleeping) ? 1 : 40);
+ }
+
+ getSavePoints()->push(kEntityVassili, kEntityAnna, kAction226031488);
+ getSavePoints()->push(kEntityVassili, kEntityVerges, kAction226031488);
+ getSavePoints()->push(kEntityVassili, kEntityCoudert, kAction226031488);
+ getSound()->playSound(kEntityVassili, "VAS1027", SoundManager::kFlagDefault);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Vassili, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ if (!getEntities()->isDistanceBetweenEntities(kEntityVassili, kEntityPlayer, 2500))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setup_seizure();
+ break;
+
+ case kActionDefault:
+ case kActionDrawScene:
+ if ((getObjects()->get(kObjectCompartmentA).location == kObjectLocation2 && getEntities()->isPlayerPosition(kCarRedSleeping, 17))
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 18)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 37)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 38)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 41)) {
+
+ if (savepoint.action == kActionDrawScene)
+ getSound()->processEntry(kEntityVassili);
+
+ setup_seizure();
+ } else {
+ if (savepoint.action == kActionDefault)
+ getSound()->playSound(kEntityVassili, "VAS1028", SoundManager::kFlagDefault);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Vassili, seizure)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Check that we have removed the body from the train and changed jacket
+ if (!getProgress().eventCorpseMovedFromFloor) {
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ if (!getProgress().eventCorpseThrown) {
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ // Setup Anna & Coudert
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function37);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function38);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliSeizure);
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getData()->location = kLocationInsideCompartment;
+ getAction()->playAnimation(kEventVassiliSeizure);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getProgress().field_18 = 2;
+
+ getSavePoints()->push(kEntityVassili, kEntityAnna, kAction191477936);
+ getSavePoints()->push(kEntityVassili, kEntityVerges, kAction191477936);
+ getSavePoints()->push(kEntityVassili, kEntityCoudert, kAction191477936);
+ getScenes()->loadSceneFromObject(kObjectCompartmentA);
+
+ setup_drawInBed();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Vassili, drawInBed)
+ if (savepoint.action == kActionDefault)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Vassili, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_sleeping();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Vassili, sleeping)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Vassili, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_stealEgg();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Vassili, stealEgg)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionOpenDoor:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliCompartmentStealEgg);
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && getInventory()->hasItem(kItemFirebird)
+ && !getEvent(kEventVassiliCompartmentStealEgg))
+ getObjects()->update(kObject48, kEntityVassili, kObjectLocationNone, kCursorNormal, kCursorHand);
+ else
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventVassiliCompartmentStealEgg);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 67);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Vassili, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Looks identical to sleeping (#13)
+IMPLEMENT_FUNCTION(17, Vassili, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Vassili, chapter5)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/vassili.h b/engines/lastexpress/entities/vassili.h
new file mode 100644
index 0000000000..1862069e25
--- /dev/null
+++ b/engines/lastexpress/entities/vassili.h
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_VASSILI_H
+#define LASTEXPRESS_VASSILI_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Vassili : public Entity {
+public:
+ Vassili(LastExpressEngine *engine);
+ ~Vassili() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function6)
+ DECLARE_FUNCTION(function7)
+ DECLARE_FUNCTION(function8)
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(seizure)
+ DECLARE_FUNCTION(drawInBed)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(sleeping)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(stealEgg)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VASSILI_H
diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp
new file mode 100644
index 0000000000..76bf646113
--- /dev/null
+++ b/engines/lastexpress/entities/verges.cpp
@@ -0,0 +1,1898 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Verges::Verges(LastExpressEngine *engine) : Entity(engine, kEntityVerges) {
+ ADD_CALLBACK_FUNCTION(Verges, reset);
+ ADD_CALLBACK_FUNCTION(Verges, draw);
+ ADD_CALLBACK_FUNCTION(Verges, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Verges, playSound);
+ ADD_CALLBACK_FUNCTION(Verges, playSound16);
+ ADD_CALLBACK_FUNCTION(Verges, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Verges, savegame);
+ ADD_CALLBACK_FUNCTION(Verges, updateEntity);
+ ADD_CALLBACK_FUNCTION(Verges, function9);
+ ADD_CALLBACK_FUNCTION(Verges, function10);
+ ADD_CALLBACK_FUNCTION(Verges, function11);
+ ADD_CALLBACK_FUNCTION(Verges, function12);
+ ADD_CALLBACK_FUNCTION(Verges, function13);
+ ADD_CALLBACK_FUNCTION(Verges, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Verges, function15);
+ ADD_CALLBACK_FUNCTION(Verges, function16);
+ ADD_CALLBACK_FUNCTION(Verges, function17);
+ ADD_CALLBACK_FUNCTION(Verges, chapter1);
+ ADD_CALLBACK_FUNCTION(Verges, talkHarem);
+ ADD_CALLBACK_FUNCTION(Verges, talkPassengerList);
+ ADD_CALLBACK_FUNCTION(Verges, talkGendarmes);
+ ADD_CALLBACK_FUNCTION(Verges, function22);
+ ADD_CALLBACK_FUNCTION(Verges, function23);
+ ADD_CALLBACK_FUNCTION(Verges, policeGettingOffTrain);
+ ADD_CALLBACK_FUNCTION(Verges, function25);
+ ADD_CALLBACK_FUNCTION(Verges, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Verges, chapter2);
+ ADD_CALLBACK_FUNCTION(Verges, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Verges, chapter3);
+ ADD_CALLBACK_FUNCTION(Verges, function30);
+ ADD_CALLBACK_FUNCTION(Verges, function31);
+ ADD_CALLBACK_FUNCTION(Verges, function32);
+ ADD_CALLBACK_FUNCTION(Verges, function33);
+ ADD_CALLBACK_FUNCTION(Verges, function34);
+ ADD_CALLBACK_FUNCTION(Verges, function35);
+ ADD_CALLBACK_FUNCTION(Verges, chapter4);
+ ADD_CALLBACK_FUNCTION(Verges, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Verges, function38);
+ ADD_CALLBACK_FUNCTION(Verges, chapter5);
+ ADD_CALLBACK_FUNCTION(Verges, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Verges, function41);
+ ADD_CALLBACK_FUNCTION(Verges, function42);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Verges, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Verges, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Verges, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityVerges);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Verges, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Verges, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Verges, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Verges, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Verges, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityPlayer, "TRA1113", getSound()->getSoundFlag(kEntityVerges));
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(9, Verges, function9)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function10(kCarGreenSleeping, kPosition_540, (char *)&params->seq1);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityVerges);
+
+ setCallback(5);
+ setup_updateFromTime(225);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param7) {
+ if (!getSound()->isBuffered(kEntityVerges)) {
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ params->param7 = 1;
+ }
+ }
+
+ if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param6) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+
+ params->param6 = 0;
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ params->param6 = 1;
+ break;
+
+ case kActionDefault:
+ if (!getSound()->isBuffered(kEntityVerges)) {
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ params->param7 = 1;
+ }
+
+ if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Verges, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_draw("813US");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceRight(kEntityVerges, "813UD");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 4: {
+ getEntities()->clearSequences(kEntityVerges);
+
+ bool loadscene = true;
+
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesEscortToDiningCar);
+ else if (getEntities()->isInBaggageCar(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesBaggageCarOffLimits);
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesCanIHelpYou);
+ else
+ loadscene = false;
+
+ if (loadscene) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Verges, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_850;
+ getEntities()->clearSequences(kEntityVerges);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(13, Verges, function13, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVergesSuitcase);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEvent(kEventVergesSuitcase) || getEvent(kEventVergesSuitcaseNight) || getEvent(kEventVergesSuitcaseOtherEntry) || getEvent(kEventVergesSuitcaseNightOtherEntry))
+ params->param2 = 1;
+
+ if (isNight() && getProgress().chapter != kChapter1)
+ params->param2 = 1;
+
+ if (params->param1) {
+ if (isNight())
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseNightOtherEntryStart : kEventVergesSuitcaseNightOtherEntry);
+ else
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseOtherEntryStart : kEventVergesSuitcaseOtherEntry);
+ } else {
+ if (isNight())
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseNightStart : kEventVergesSuitcaseNight);
+ else
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseStart : kEventVergesSuitcase);
+ }
+
+ getEntities()->clearSequences(kEntityVerges);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 91);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Verges, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param5 && params->param6) {
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ params->param5 = 1;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620F");
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction171394341);
+ break;
+
+ case kAction155853632:
+ params->param6 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (CURRENT_PARAM(1, 1) && params->param8) {
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ CURRENT_PARAM(1, 1)++;
+
+ if (CURRENT_PARAM(1, 1) == 1)
+ getSound()->playSound(kEntityVerges, (char *)&params->seq2);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620F");
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction171394341);
+ break;
+
+ case kAction155853632:
+ params->param8 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Verges, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityMertens, "TRA1291");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ ENTITY_PARAM(0, 3) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Verges, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityVerges, kActionDeliverMessageToTyler, 0);
+ getSavePoints()->addData(kEntityVerges, kAction226031488, 1);
+ getSavePoints()->addData(kEntityVerges, kAction339669520, 1);
+ getSavePoints()->addData(kEntityVerges, kAction167854368, 4);
+ getSavePoints()->addData(kEntityVerges, kAction158617345, 2);
+ getSavePoints()->addData(kEntityVerges, kAction168255788, 3);
+ getSavePoints()->addData(kEntityVerges, kAction201431954, 5);
+ getSavePoints()->addData(kEntityVerges, kAction168187490, 6);
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(19, Verges, talkHarem)
+ talk(savepoint, "TRA1202", "TRA1201");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Verges, talkPassengerList)
+ talk(savepoint, "TRA1205", "TRA1206");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Verges, talkGendarmes)
+ talk(savepoint, "TRA1250", "TRA1251");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Verges, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ if (getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAugustWaiting)) {
+ setCallback(3);
+ setup_function16(kEntityMertens, "TRA1200", "TRA1201");
+ } else {
+ setCallback(4);
+ setup_function16(kEntityMertens, "TRA1200A", "TRA1201");
+ }
+ break;
+
+ case 3:
+ case 4:
+ getSavePoints()->push(kEntityVerges, kEntityMertens, kAction169633856);
+
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Verges, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction191477936:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityVerges, kEntityPlayer, 1000) && getEntityData(kEntityPlayer)->location == kLocationOutsideCompartment) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityVerges, "POL1101", SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSound()->processEntry(kEntityVerges);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Verges, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ if (!getEntities()->isInKronosSalon(kEntityPlayer)) {
+
+ if (getEntityData(kEntityPlayer)->car > kCarRedSleeping
+ || (getEntityData(kEntityPlayer)->car == kCarRedSleeping && getEntityData(kEntityPlayer)->entityPosition > kPosition_9270)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ } else {
+ if (getEntityData(kEntityPlayer)->car > kCarGreenSleeping
+ || (getEntityData(kEntityPlayer)->car == kCarGreenSleeping && getEntityData(kEntityPlayer)->entityPosition < kPosition_4840)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ }
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ }
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isOutsideAnnaWindow())
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4840)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentE, true);
+ }
+
+ getSavePoints()->push(kEntityVerges, kEntityGendarmes, kAction169499649);
+
+ getProgress().field_3C = 1;
+ getState()->timeDelta = 1;
+
+ if (getData()->car == kCarRedSleeping) {
+ setCallback(6);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA1005");
+ } else {
+ setCallback(7);
+ setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ }
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ if (getEvent(kEventKronosConversation)) {
+ getProgress().field_3C = 1;
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+
+ getState()->timeDelta = 3;
+ getSavePoints()->push(kEntityVerges, kEntityChapters, kAction169629818);
+
+ setCallback(3);
+ setup_policeGettingOffTrain();
+ } else {
+ setCallback(2);
+ setup_updateFromTime(150);
+ }
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
+
+ setCallback(4);
+ setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case 5:
+ case 11:
+ ENTITY_PARAM(0, 7) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ case 7:
+ getEntities()->clearSequences(kEntityVerges);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityVerges, kEntityChapters, kAction169629818);
+
+ setCallback(9);
+ setup_policeGettingOffTrain();
+ break;
+
+ case 9:
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
+
+ setCallback(10);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA1006");
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function11();
+ break;
+ }
+ break;
+
+ case kAction168710784:
+ getData()->car = kCarGreenSleeping;
+
+ if (!(getEntityData(kEntityPlayer)->car == kCarGreenSleeping))
+ getData()->car = kCarRedSleeping;
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+
+ getState()->timeDelta = 3;
+
+ setCallback(8);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Verges, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 6)) {
+ params->param1 = 1;
+ params->param2 = 1;
+ params->param3 = 1;
+ params->param4 = 1;
+ params->param5 = 1;
+ params->param6 = 1;
+
+ ENTITY_PARAM(0, 6) = 0;
+ }
+
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(1);
+ setup_function23();
+ break;
+ }
+
+label_callback1:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(2);
+ setup_function13(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(3);
+ setup_function25();
+ break;
+ }
+
+label_callback3:
+ if (params->param6)
+ goto label_callback12;
+
+ TIME_CHECK_CALLBACK_1(kTimeChapter1, params->param7, 4, setup_function9, "TRA1001");
+
+label_callback4:
+ TIME_CHECK_CALLBACK(kTime1089000, params->param8, 5, setup_function12);
+
+ params->param8 = 1;
+
+ if (!params->param5) {
+ setCallback(5);
+ setup_function12();
+ break;
+ }
+
+label_callback8:
+ TIME_CHECK_CALLBACK_1(kTime1107000, CURRENT_PARAM(1, 1), 9, setup_function9, "TRA1001A");
+
+label_callback9:
+ TIME_CHECK_CALLBACK_1(kTime1134000, CURRENT_PARAM(1, 2), 10, setup_function9, "TRA1002");
+
+label_callback10:
+ TIME_CHECK_CALLBACK_1(kTime1165500, CURRENT_PARAM(1, 3), 11, setup_function9, "TRA1003");
+
+label_callback11:
+ TIME_CHECK_CALLBACK_1(kTime1225800, CURRENT_PARAM(1, 4), 12, setup_function9, "TRA1004");
+
+label_callback12:
+ if (ENTITY_PARAM(0, 5) && !params->param2) {
+ setCallback(13);
+ setup_talkGendarmes();
+ break;
+ }
+
+label_callback13:
+ if (getInventory()->hasItem(kItemPassengerList) && !params->param3 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
+ setCallback(14);
+ setup_talkPassengerList();
+ break;
+ }
+
+label_callback14:
+ if (ENTITY_PARAM(0, 3) && !params->param4 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
+ setCallback(15);
+ setup_function17();
+ break;
+ }
+
+label_callback15:
+ if (ENTITY_PARAM(0, 1) && !params->param5) {
+ if (getState()->time < kTime1134000 || getState()->time > kTime1156500) {
+ setCallback(16);
+ setup_function22();
+ }
+ }
+ break;
+
+ case kActionOpenDoor:
+ setCallback(17);
+ setup_function13(savepoint.param.intValue < 106 ? true : false);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityVerges);
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function15(kEntityMertens, "TRA1202");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function11();
+ break;
+
+ case 8:
+ goto label_callback8;
+
+ case 9:
+ goto label_callback9;
+
+ case 10:
+ goto label_callback10;
+
+ case 11:
+ goto label_callback11;
+
+ case 12:
+ goto label_callback12;
+
+ case 13:
+ params->param2 = 1;
+ goto label_callback13;
+
+ case 14:
+ params->param3 = 1;
+ goto label_callback14;
+
+ case 15:
+ params->param4 = 1;
+ goto label_callback15;
+
+ case 16:
+ params->param5 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Verges, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Verges, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ }
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTime1818900, params->param1, 2, setup_function9, "Tra2177");
+
+label_callback_2:
+ if (params->param2 == kTimeInvalid || !getState()->time)
+ goto label_callback_6;
+
+ if (getState()->time > kTime1836000) {
+ params->param2 = kTimeInvalid;
+ setCallback(3);
+ setup_function12();
+ break;
+ }
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param2) {
+ params->param2 = (uint)getState()->time;
+
+ if (!params->param2) {
+ setCallback(3);
+ setup_function12();
+ break;
+ }
+ }
+
+ if (params->param2 >= getState()->time) {
+label_callback_6:
+
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(7);
+ setup_function17();
+ }
+
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+ setCallback(3);
+ setup_function12();
+ break;
+
+ case kActionOpenDoor:
+ setCallback(8);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionDefault:
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityCoudert, "TRA2100");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ goto label_callback_6;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Verges, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(30, Verges, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Verges, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, "TRA3015");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ getProgress().field_48 = 1;
+ ENTITY_PARAM(0, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Verges, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_3(kTime2263500, params->param1, 5, setup_function10, kCarRedSleeping, kPosition_9460, "TRA3006");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_8500;
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityVerges, "TRA3004");
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, "TRA3004");
+
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA3004");
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityVerges);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Verges, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("813US");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVerges, "813UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityVerges);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5799;
+
+ setCallback(getProgress().field_3C ? 4 : 5);
+ setup_playSound(getProgress().field_3C ? "ABB3035A" : "ABB3035");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("ABB3035");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityVerges, kEntityAbbot, kAction192054567);
+
+ setCallback(6);
+ setup_function9("Tra3010");
+ break;
+
+ case 6:
+ setup_function34();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Verges, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(2);
+ setup_function31();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function17();
+ break;
+ }
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 4, setup_function9, "Tra3001");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime1998000, params->param2, 5, setup_function9, "Tra3010a");
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2016000, params->param3, 6, setup_function35);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime2070000, params->param4, 7, setup_function9, "Tra3002");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTime2142000, params->param5, 8, setup_function9, "Tra3003");
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2173500, params->param6, 9, setup_function30, "Tra3012");
+
+label_callback_9:
+ TIME_CHECK_CALLBACK(kTime2218500, params->param7, 10, setup_function32);
+ break;
+
+ case kActionOpenDoor:
+ setCallback(11);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Verges, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityMertens, "Tra3011A");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction188570113);
+
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityMertens, "Tra3011");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityVerges, kEntityMertens, kAction188635520);
+
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Verges, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Verges, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 6)) {
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(2);
+ setup_function17();
+ break;
+ }
+
+label_callback_2:
+ TIME_CHECK_CALLBACK_1(kTime2349000, params->param1, 3, setup_function9, "Tra1001");
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTime2378700, params->param2, 4, setup_function9, "Tra4001");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime2403000, params->param3, 5, setup_function9, "Tra1001A");
+
+label_callback_5:
+ TIME_CHECK_CALLBACK_1(kTime2414700, params->param4, 6, setup_function9, "Tra4002");
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime2484000, params->param5, 7, setup_function9, "Tra4003");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTime2511000, params->param6, 8, setup_function9, "Tra4004");
+ }
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2538000, params->param7, 9, setup_function9, "Tra4005");
+
+ break;
+
+ case kActionOpenDoor:
+ setCallback(10);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Verges, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_6469;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityVerges);
+ setCallback(2);
+ setup_updateFromTime(1800);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11();
+ break;
+
+ case 3:
+ setup_chapter4Handler();
+ break;
+ }
+ break;
+
+ case kAction125233040:
+ getData()->entityPosition = kPosition_5790;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Verges, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Verges, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInSalon(kEntityPlayer) && !getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, "WAT5000");
+ break;
+
+ case kActionOpenDoor:
+ if (getSound()->isBuffered(kEntityVerges))
+ getSound()->processEntry(kEntityVerges);
+
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathFreePassengers);
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getObjects()->update(kObject65, kEntityVerges, kObjectLocation1, kCursorNormal, kCursorForward);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCathFreePassengers);
+ getSavePoints()->pushAll(kEntityVerges, kActionProceedChapter5);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
+ setup_function41();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Verges, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation3);
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_function10(kCarRedSleeping, kPosition_2000, "Tra5001");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ // Fallback to next case
+
+ case 2:
+ if (getSound()->isBuffered(kEntityVerges)) {
+ setCallback(2);
+ setup_updateFromTime(225);
+ } else {
+ setCallback(3);
+ setup_playSound("Con5001");
+ }
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction155991520);
+
+ setCallback(4);
+ setup_updateEntity(kCarBaggageRear, kPosition_9460);
+ break;
+
+ case 4:
+ setup_function42();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Verges, function42)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityVerges);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *sound2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, sound1);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityMertens, sound2);
+ break;
+
+ case 5:
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h
new file mode 100644
index 0000000000..40a43eac9d
--- /dev/null
+++ b/engines/lastexpress/entities/verges.h
@@ -0,0 +1,182 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_VERGES_H
+#define LASTEXPRESS_VERGES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Verges : public Entity {
+public:
+ Verges(LastExpressEngine *engine);
+ ~Verges() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound16)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function9, const char *soundName)
+ DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, const char *soundName)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION_1(function13, bool)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION_2(function15, EntityIndex entity, const char *soundName)
+ DECLARE_FUNCTION_3(function16, EntityIndex entityIndex, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_NOSETUP(talkHarem)
+ DECLARE_FUNCTION(talkPassengerList)
+ DECLARE_FUNCTION(talkGendarmes)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(policeGettingOffTrain)
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION_1(function30, const char *soundName)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function38)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function41)
+ DECLARE_FUNCTION(function42)
+
+private:
+ void talk(const SavePoint &savepoint, const char *sound1, const char *sound2);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VERGES_H
diff --git a/engines/lastexpress/entities/vesna.cpp b/engines/lastexpress/entities/vesna.cpp
new file mode 100644
index 0000000000..0e8d3bda12
--- /dev/null
+++ b/engines/lastexpress/entities/vesna.cpp
@@ -0,0 +1,1161 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/vesna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Vesna::Vesna(LastExpressEngine *engine) : Entity(engine, kEntityVesna) {
+ ADD_CALLBACK_FUNCTION(Vesna, reset);
+ ADD_CALLBACK_FUNCTION(Vesna, playSound);
+ ADD_CALLBACK_FUNCTION(Vesna, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Vesna, draw);
+ ADD_CALLBACK_FUNCTION(Vesna, updateEntity);
+ ADD_CALLBACK_FUNCTION(Vesna, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Vesna, updateEntity2);
+ ADD_CALLBACK_FUNCTION(Vesna, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Vesna, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Vesna, savegame);
+ ADD_CALLBACK_FUNCTION(Vesna, function11);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter1);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function14);
+ ADD_CALLBACK_FUNCTION(Vesna, function15);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter2);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function18);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter3);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function21);
+ ADD_CALLBACK_FUNCTION(Vesna, function22);
+ ADD_CALLBACK_FUNCTION(Vesna, function23);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter4);
+ ADD_CALLBACK_FUNCTION(Vesna, function25);
+ ADD_CALLBACK_FUNCTION(Vesna, function26);
+ ADD_CALLBACK_FUNCTION(Vesna, function27);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter5);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function30);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Vesna, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Vesna, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Vesna, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Vesna, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Vesna, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT10150" : "CAT1015A");
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Vesna, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Vesna, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ params->param3 = 0;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityVesna, kEntityMilos, 500)
+ || (((getData()->direction == kDirectionUp && (getData()->car > getEntityData(kEntityMilos)->car)) || (getData()->car == getEntityData(kEntityMilos)->car && getData()->entityPosition > getEntityData(kEntityMilos)->entityPosition)))
+ || (((getData()->direction == kDirectionDown && (getData()->car < getEntityData(kEntityMilos)->car)) || (getData()->car == getEntityData(kEntityMilos)->car && getData()->entityPosition < getEntityData(kEntityMilos)->entityPosition)))) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntityVesna, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntityVesna, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Vesna, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Vesna, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Vesna, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Vesna, function11)
+ // Expose parameters as IIIS and ignore the default exposed parameters
+ EntityData::EntityParametersIIIS *parameters = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (parameters->param3) {
+ UPDATE_PARAM(parameters->param7, getState()->timeTicks, 75);
+
+ parameters->param2 = 1;
+ parameters->param3 = 0;
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ parameters->param7 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (parameters->param3) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ parameters->param1++;
+ switch (parameters->param1) {
+ default:
+ strcpy((char *)&parameters->seq, "VES1015C");
+ parameters->param1 = 0;
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq, "VES1015A");
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq, "VES1015B");
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (parameters->param2 || parameters->param3) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ parameters->param2 = 0;
+ parameters->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound((char *)&parameters->seq);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorTalk, kCursorNormal);
+ parameters->param3 = 1;
+ break;
+
+ case 4:
+ parameters->param2 = 1;
+ parameters->param3 = 0;
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ case kAction101687594:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Vesna, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityVesna, kAction124190740, 0);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Vesna, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityMilos)->entityPosition;
+ getData()->location = getEntityData(kEntityMilos)->location;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->clearSequences(kEntityVesna);
+ setup_function14();
+ }
+ break;
+
+ case kAction204832737:
+ setCallback(1);
+ setup_updateEntity2(kCarRedSleeping, kPosition_3050);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Vesna, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction190412928:
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Vesna, function15)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityVesna);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Vesna, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Vesna, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction135024800:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case kAction137165825:
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Vesna, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 7:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_draw("808DD");
+ break;
+
+ case 8:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(9);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG);
+ break;
+
+ case 11:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Vesna, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Vesna, chapter3Handler)
+ EntityData::EntityParametersIIIS *parameters = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_54 && parameters->param7 != kTimeInvalid) {
+ if (getState()->time > kTime2250000) {
+ parameters->param7 = kTimeInvalid;
+ setup_function22();
+ break;
+ }
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !parameters->param7)
+ parameters->param7 = (uint)getState()->time;
+
+ if (parameters->param7 < getState()->time) {
+ parameters->param7 = kTimeInvalid;
+ setup_function22();
+ break;
+ }
+ }
+
+ if (parameters->param2) {
+ UPDATE_PARAM(parameters->param8, getState()->timeTicks, 75);
+
+ parameters->param1 = 1;
+ parameters->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ parameters->param8 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (parameters->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ ++parameters->param3;
+
+ switch (parameters->param3) {
+ default:
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq, "VES1015A");
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq, "VES1015B");
+ break;
+
+ case 3:
+ strcpy((char *)&parameters->seq, "VES1015C");
+ parameters->param3 = 0;
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->clearSequences(kEntityVesna);
+ break;
+
+ case kActionDrawScene:
+ if (parameters->param1 || parameters->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ parameters->param1 = 0;
+ parameters->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound((char *)&parameters->seq);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorTalk, kCursorNormal);
+ parameters->param2 = 1;
+ break;
+
+ case 4:
+ parameters->param1 = 1;
+ parameters->param2 = 0;
+ break;
+ }
+ break;
+
+ case kAction137165825:
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case kAction155913424:
+ setCallback(6);
+ setup_function21();
+ break;
+
+ case kAction203663744:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Vesna, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 7:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_draw("808DD");
+ break;
+
+ case 8:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(9);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG); /* BUG the original engine passes 3050 here */
+ break;
+
+ case 11:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Vesna, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityVesna, kEntityMilos, kAction259125998);
+
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->car = kCarBaggage;
+ getSavePoints()->push(kEntityVesna, kEntityAnna, kAction235856512);
+ break;
+
+ case 6:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_draw("808DD");
+ break;
+
+ case 7:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(8);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG); /* BUG the original engine passes 3050 here */
+ break;
+
+ case 10:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ setup_function23();
+ break;
+ }
+ break;
+
+ case kAction189299008:
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Vesna, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("VES1015A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction203663744:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Vesna, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function11();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function25();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Vesna, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2428200 && !params->param1) {
+ params->param1 = 1;
+ setup_function26();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityVesna, kEntityMilos, kAction135600432);
+
+ setCallback(1);
+ setup_enterExitCompartment("610BG", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+
+ // Original game calls clearSequences a second time
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Vesna, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("808DD");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("610AG", kObjectCompartmentG);
+ break;
+
+ case 5:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Vesna, function27)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityVesna);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Vesna, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Vesna, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionOpenDoor:
+ setCallback(1);
+
+ getData()->currentCall++;
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaRestaurantKilled);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject64, kEntityVesna, kObjectLocationNone, kCursorNormal, kCursorForward);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCathVesnaRestaurantKilled);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ }
+ break;
+
+ case kAction134427424:
+ getObjects()->update(kObject64, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ setup_function30();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Vesna, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->timeTicks, 120)
+ getSound()->playSound(kEntityVesna, "Ves50001", SoundManager::kFlagDefault);
+ params->param1 = 1;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 180);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getAction()->playAnimation(kEventCathVesnaTrainTopKilled);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathVesnaTrainTopFight);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 4:
+ params->param2 = getFight()->setup(kFightVesna);
+
+ if (params->param2) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param2 != Fight::kFightEndExit);
+ } else {
+ getSound()->playSound(kEntityPlayer, "TUNNEL");
+
+ getState()->time = (TimeValue)(getState()->time + 1800);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopWin);
+ }
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventCathVesnaTrainTopWin);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 11);
+
+ setup_nullfunction();
+ break;
+ }
+ break;
+
+ case kAction167992577:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopFight);
+ break;
+
+ case kAction202884544:
+ if (params->param1) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled);
+ } else {
+ getSound()->playSound(kEntityVesna, "Ves5001", SoundManager::kFlagDefault);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(31, Vesna)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/vesna.h b/engines/lastexpress/entities/vesna.h
new file mode 100644
index 0000000000..55a9a989c5
--- /dev/null
+++ b/engines/lastexpress/entities/vesna.h
@@ -0,0 +1,176 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_VESNA_H
+#define LASTEXPRESS_VESNA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Vesna : public Entity {
+public:
+ Vesna(LastExpressEngine *engine);
+ ~Vesna() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VESNA_H
diff --git a/engines/lastexpress/entities/yasmin.cpp b/engines/lastexpress/entities/yasmin.cpp
new file mode 100644
index 0000000000..7dc46f9690
--- /dev/null
+++ b/engines/lastexpress/entities/yasmin.cpp
@@ -0,0 +1,490 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/yasmin.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Yasmin::Yasmin(LastExpressEngine *engine) : Entity(engine, kEntityYasmin) {
+ ADD_CALLBACK_FUNCTION(Yasmin, reset);
+ ADD_CALLBACK_FUNCTION(Yasmin, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Yasmin, playSound);
+ ADD_CALLBACK_FUNCTION(Yasmin, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Yasmin, updateEntity);
+ ADD_CALLBACK_FUNCTION(Yasmin, function6);
+ ADD_CALLBACK_FUNCTION(Yasmin, function7);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter1);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function10);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter2);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter3);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter4);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function17);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter5);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function20);
+ ADD_CALLBACK_FUNCTION(Yasmin, function21);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Yasmin, reset)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityYasmin);
+ break;
+ }
+
+ // Process default actions
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Yasmin, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Yasmin, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Yasmin, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Yasmin, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Yasmin, function6)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("615Be", kObjectCompartment5);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_3050);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("615Ag", kObjectCompartment7);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityYasmin);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Yasmin, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("615Bg", kObjectCompartment7);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4840);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("615Ae", kObjectCompartment5);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityYasmin);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Yasmin, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1093500, params->param1, 1, setup_function6);
+ TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7);
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070);
+ TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104");
+ TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106");
+ TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_2740;
+ setCallback(2);
+ setup_playSound("Har1102");
+ break;
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7);
+ // Fallback to case 3
+
+ case 3:
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070);
+ // Fallback to case 4
+
+ case 4:
+ TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104");
+ // Fallback to case 5
+
+ case 5:
+ TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106");
+ // Fallback to case 6
+
+ case 6:
+ TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Yasmin, function10)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityYasmin);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Yasmin, chapter2)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Yasmin, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1759500, params->param1, 1, setup_function7);
+
+ if (getState()->time > kTime1800000 && !params->param2) {
+ params->param2 = 1;
+ getData()->entityPosition = kPosition_4070;
+
+ getSavePoints()->push(kEntityYasmin, kEntityTrain, kAction191070912, kPosition_4070);
+ }
+ break;
+
+ case kActionCallback:
+
+ if (getCallback() != 1)
+ break;
+
+ if (getState()->time > kTime1800000 && !params->param2) {
+ params->param2 = 1;
+ getData()->entityPosition = kPosition_4070;
+
+ getSavePoints()->push(kEntityYasmin, kEntityTrain, kAction191070912, kPosition_4070);
+ }
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Yasmin, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2062800, params->param1, 1, setup_function6);
+ TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7);
+ TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7);
+ // Fallback to case 2
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Yasmin, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2457000, params->param1, 1, setup_function7);
+ TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4070;
+ setCallback(2);
+ setup_playSound("Har1110");
+ break;
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Yasmin, function17)
+ // Same as existing function 10 ?
+ function10(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Yasmin, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Yasmin, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function20();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Yasmin, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function21();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setup_function21();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Yasmin, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityYasmin, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityYasmin);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/yasmin.h b/engines/lastexpress/entities/yasmin.h
new file mode 100644
index 0000000000..b35c713f8b
--- /dev/null
+++ b/engines/lastexpress/entities/yasmin.h
@@ -0,0 +1,142 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_YASMIN_H
+#define LASTEXPRESS_YASMIN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Yasmin : public Entity {
+public:
+ Yasmin(LastExpressEngine *engine);
+ ~Yasmin() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function6)
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function10)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_YASMIN_H
diff --git a/engines/lastexpress/eventhandler.h b/engines/lastexpress/eventhandler.h
new file mode 100644
index 0000000000..6f28984d2c
--- /dev/null
+++ b/engines/lastexpress/eventhandler.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_EVENTHANDLER_H
+#define LASTEXPRESS_EVENTHANDLER_H
+
+#include "common/func.h"
+#include "common/events.h"
+
+namespace LastExpress {
+
+#define SET_EVENT_HANDLERS(class, inst) \
+ _engine->setEventHandlers(new EVENT_HANDLER(class, eventMouse, inst), new EVENT_HANDLER(class, eventTick, inst));
+
+#define EVENT_HANDLER(class, name, inst) \
+ Common::Functor1Mem<const Common::Event&, void, class>(inst, &class::name)
+
+class EventHandler {
+public:
+ virtual ~EventHandler() {}
+
+ // Function pointer for event handler
+ typedef Common::Functor1<const Common::Event&, void> EventFunction;
+
+ virtual void eventMouse(const Common::Event &ev) {} // Event type 1
+ virtual void eventTick(const Common::Event &ev) {} // Event type 3
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_EVENTHANDLER_H
diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp
new file mode 100644
index 0000000000..d14e295cb3
--- /dev/null
+++ b/engines/lastexpress/game/action.cpp
@@ -0,0 +1,1968 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/action.h"
+
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+static const int _animationListSize = 273;
+
+// List of animations
+static const struct {
+ const char *filename;
+ uint16 time;
+} _animationList[_animationListSize] = {
+ {"", 0},
+ {"1002", 255},
+ {"1002D", 255},
+ {"1003", 0},
+ {"1005", 195},
+ {"1006", 750}, // 5
+ {"1006A", 750},
+ {"1008", 765},
+ {"1008N", 765},
+ {"1008A", 750},
+ {"1008AN", 750}, // 10
+ {"1009", 0},
+ {"1011", 1005},
+ {"1011A", 780},
+ {"1012", 300},
+ {"1013", 285},
+ {"1017", 870}, // 15
+ {"1017A", 0}, // Not in the data files?
+ {"1019", 120},
+ {"1019D", 120},
+ {"1020", 120}, // 20
+ {"1022", 525},
+ {"1022A", 180},
+ {"1022AD", 210},
+ {"1022B", 210},
+ {"1022C", 210}, // 25
+ {"1023", 135},
+ {"1025", 945},
+ {"1028", 300},
+ {"1030", 390},
+ {"1031", 375}, // 30
+ {"1032", 1050},
+ {"1033", 945},
+ {"1034", 495},
+ {"1035", 1230},
+ {"1037", 1425}, // 35
+ {"1038", 195},
+ {"1038A", 405},
+ {"1039", 600},
+ {"1040", 945},
+ {"1041", 510}, // 40
+ {"1042", 540},
+ {"1043", 855},
+ {"1044", 645},
+ {"1046", 0},
+ {"1047", 0}, // 45
+ {"1047A", 0},
+ {"1059", 1005},
+ {"1060", 255},
+ {"1063", 0},
+ {"1101", 255}, // 50
+ {"1102", 1320},
+ {"1103", 210},
+ {"1104", 120},
+ {"1105", 1350},
+ {"1106", 315}, // 55
+ {"1106A", 315},
+ {"1106D", 315},
+ {"1107", 1},
+ {"1107A", 660},
+ {"1108", 300}, // 60
+ {"1109", 1305},
+ {"1110", 300},
+ {"1112", 0},
+ {"1115", 0},
+ {"1115A", 0}, // 65
+ {"1115B", 0},
+ {"1115C", 0},
+ {"1115D", 0},
+ {"1115E", 0},
+ {"1115F", 0}, // 70
+ {"1115G", 0},
+ {"1115H", 0},
+ {"1116", 0},
+ {"1117", 0},
+ {"1118", 105}, // 75
+ {"1202", 510},
+ {"1202A", 510},
+ {"1203", 720},
+ {"1204", 120},
+ {"1205", 465}, // 80
+ {"1206", 690},
+ {"1206A", 450},
+ {"1208", 465},
+ {"1210", 1020},
+ {"1211", 600}, // 85
+ {"1212", 435},
+ {"1213", 525},
+ {"1213A", 150},
+ {"1215", 390},
+ {"1216", 0}, // 90
+ {"1219", 240},
+ {"1222", 1095},
+ {"1223", 0},
+ {"1224", 720},
+ {"1225", 1005}, // 95
+ {"1227", 840},
+ {"1227A", 840},
+ {"1303", 450},
+ {"1303N", 450},
+ {"1304", 450}, // 100
+ {"1304N", 450},
+ {"1305", 630},
+ {"1309", 0},
+ {"1311", 1710},
+ {"1312", 240}, // 105
+ {"1312D", 240},
+ {"1313", 930},
+ {"1315", 1035},
+ {"1315A", 1035},
+ {"1401", 540}, // 110
+ {"1402", 150},
+ {"1402B", 150},
+ {"1403", 90},
+ {"1404", 885},
+ {"1404A", 0}, // 115
+ {"1405", 135},
+ {"1406", 1665},
+ {"1501", 285},
+ {"1501A", 285},
+ {"1502", 165}, // 120
+ {"1502A", 165},
+ {"1502D", 165},
+ {"1503", 0},
+ {"1504", 0},
+ {"1505", 0}, // 125
+ {"1505A", 0},
+ {"1506", 300},
+ {"1506A", 180},
+ {"1508", 0},
+ {"1509", 450}, // 130
+ {"1509S", 450},
+ {"1509A", 450},
+ {"1509AS", 450},
+ {"1509N", 450},
+ {"1509SN", 450}, // 135
+ {"1509AN", 450},
+ {"1509BN", 450},
+ {"1511", 150},
+ {"1511A", 150},
+ {"1511B", 90}, // 140
+ {"1511BA", 90},
+ {"1511C", 135},
+ {"1511D", 105},
+ {"1930", 0},
+ {"1511E", 150}, // 145
+ {"1512", 165},
+ {"1513", 180},
+ {"1517", 0},
+ {"1517A", 165},
+ {"1518", 165}, // 150
+ {"1518A", 165},
+ {"1518B", 165},
+ {"1591", 450},
+ {"1592", 450},
+ {"1593", 450}, // 155
+ {"1594", 450},
+ {"1595", 450},
+ {"1596", 450},
+ {"1601", 0},
+ {"1603", 0}, // 160
+ {"1606B", 315},
+ {"1607A", 0},
+ {"1610", 0},
+ {"1611", 0},
+ {"1612", 0}, // 165
+ {"1615", 0},
+ {"1619", 0},
+ {"1620", 120},
+ {"1621", 105},
+ {"1622", 105}, // 170
+ {"1629", 450},
+ {"1630", 450},
+ {"1631", 525},
+ {"1632", 0},
+ {"1633", 615}, // 175
+ {"1634", 180},
+ {"1702", 180},
+ {"1702DD", 180},
+ {"1702NU", 180},
+ {"1702ND", 180}, // 180
+ {"1704", 300},
+ {"1704D", 300},
+ {"1705", 195},
+ {"1705D", 195},
+ {"1706", 195}, // 185
+ {"1706DD", 195},
+ {"1706ND", 195},
+ {"1706NU", 195},
+ {"1901", 135},
+ {"1902", 1410}, // 190
+ {"1903", 0},
+ {"1904", 1920},
+ {"1908", 600},
+ {"1908A", 195},
+ {"1908B", 105}, // 195
+ {"1908C", 165},
+ {"1908CD", 0},
+ {"1909A", 150},
+ {"1909B", 150},
+ {"1909C", 150}, // 200
+ {"1910A", 180},
+ {"1910B", 180},
+ {"1910C", 180},
+ {"1911A", 90},
+ {"1911B", 90}, // 205
+ {"1911C", 90},
+ {"1912", 0},
+ {"1913", 0},
+ {"1917", 0},
+ {"1918", 390}, // 210
+ {"1919", 360},
+ {"1919A", 105},
+ {"1920", 75},
+ {"1922", 75},
+ {"1923", 150}, // 215
+ {"8001", 120},
+ {"8001A", 120},
+ {"8002", 120},
+ {"8002A", 120},
+ {"8002B", 120}, // 220
+ {"8003", 105},
+ {"8003A", 105},
+ {"8004", 105},
+ {"8004A", 105},
+ {"8005", 270}, // 225
+ {"8005B", 270},
+ {"8010", 270},
+ {"8013", 120},
+ {"8013A", 120},
+ {"8014", 165}, // 230
+ {"8014A", 165},
+ {"8014R", 165},
+ {"8014AR", 165},
+ {"8015", 150},
+ {"8015A", 150}, // 235
+ {"8015R", 150},
+ {"8015AR", 150},
+ {"8017", 120},
+ {"8017A", 120},
+ {"8017R", 120}, // 240
+ {"8017AR", 120},
+ {"8017N", 90},
+ {"8023", 135},
+ {"8023A", 135},
+ {"8023M", 135}, // 245
+ {"8024", 150},
+ {"8024A", 180},
+ {"8024M", 180},
+ {"8025", 150},
+ {"8025A", 150}, // 250
+ {"8025M", 150},
+ {"8027", 75},
+ {"8028", 75},
+ {"8029", 120},
+ {"8029A", 120}, // 255
+ {"8031", 375},
+ {"8032", 0},
+ {"8032A", 0},
+ {"8033", 105},
+ {"8035", 195}, // 260
+ {"8035A", 120},
+ {"8035B", 180},
+ {"8035C", 135},
+ {"8036", 105},
+ {"8037", 195}, // 265
+ {"8037A", 195},
+ {"8040", 240},
+ {"8040A", 240},
+ {"8041", 195},
+ {"8041A", 195}, // 270
+ {"8042", 600},
+ {"8042A", 600}
+};
+
+Action::Action(LastExpressEngine *engine) : _engine(engine) {
+ ADD_ACTION(dummy);
+ ADD_ACTION(inventory);
+ ADD_ACTION(savePoint);
+ ADD_ACTION(playSound);
+ ADD_ACTION(playMusic);
+ ADD_ACTION(knock);
+ ADD_ACTION(compartment);
+ ADD_ACTION(playSounds);
+ ADD_ACTION(playAnimation);
+ ADD_ACTION(openCloseObject);
+ ADD_ACTION(updateObjetLocation2);
+ ADD_ACTION(setItemLocation);
+ ADD_ACTION(knockNoSound);
+ ADD_ACTION(pickItem);
+ ADD_ACTION(dropItem);
+ ADD_ACTION(dummy);
+ ADD_ACTION(enterCompartment);
+ ADD_ACTION(dummy);
+ ADD_ACTION(getOutsideTrain);
+ ADD_ACTION(slip);
+ ADD_ACTION(getInsideTrain);
+ ADD_ACTION(climbUpTrain);
+ ADD_ACTION(climbDownTrain);
+ ADD_ACTION(jumpUpDownTrain);
+ ADD_ACTION(unbound);
+ ADD_ACTION(25);
+ ADD_ACTION(26);
+ ADD_ACTION(27);
+ ADD_ACTION(concertSitCough);
+ ADD_ACTION(29);
+ ADD_ACTION(catchBeetle);
+ ADD_ACTION(exitCompartment);
+ ADD_ACTION(32);
+ ADD_ACTION(useWhistle);
+ ADD_ACTION(openMatchBox);
+ ADD_ACTION(openBed);
+ ADD_ACTION(dummy);
+ ADD_ACTION(dialog);
+ ADD_ACTION(eggBox);
+ ADD_ACTION(39);
+ ADD_ACTION(bed);
+ ADD_ACTION(playMusicChapter);
+ ADD_ACTION(playMusicChapterSetupTrain);
+ ADD_ACTION(switchChapter);
+ ADD_ACTION(44);
+}
+
+Action::~Action() {
+ for (int i = 0; i < (int)_actions.size(); i++)
+ SAFE_DELETE(_actions[i]);
+
+ _actions.clear();
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Processing hotspot
+//////////////////////////////////////////////////////////////////////////
+SceneIndex Action::processHotspot(const SceneHotspot &hotspot) {
+ if (!hotspot.action || hotspot.action >= (int)_actions.size())
+ return kSceneInvalid;
+
+ return (*_actions[hotspot.action])(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Actions
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+// Action 0
+IMPLEMENT_ACTION(dummy)
+ warning("Action::action_dummy: Dummy action function called (hotspot action: %d)!", hotspot.action);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 1
+IMPLEMENT_ACTION(inventory)
+ if (!getState()->sceneUseBackup)
+ return kSceneInvalid;
+
+ SceneIndex index = kSceneNone;
+ if (getState()->sceneBackup2) {
+ index = getState()->sceneBackup2;
+ getState()->sceneBackup2 = kSceneNone;
+ } else {
+ getState()->sceneUseBackup = false;
+ index = getState()->sceneBackup;
+
+ Scene *backup = getScenes()->get(getState()->sceneBackup);
+ if (getEntities()->getPosition(backup->car, backup->position))
+ index = getScenes()->processIndex(getState()->sceneBackup);
+ }
+
+ getScenes()->loadScene(index);
+
+ if (!getInventory()->getSelectedItem())
+ return kSceneInvalid;
+
+ if (!getInventory()->getSelectedEntry()->isSelectable || (!getState()->sceneBackup2 && getInventory()->getFirstExaminableItem()))
+ getInventory()->selectItem(getInventory()->getFirstExaminableItem());
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 2
+IMPLEMENT_ACTION(savePoint)
+ getSavePoints()->push(kEntityPlayer, (EntityIndex)hotspot.param1, (ActionIndex)hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 3
+IMPLEMENT_ACTION(playSound)
+
+ // Check that the file is not already buffered
+ if (hotspot.param2 || !getSound()->isBuffered(Common::String::format("LIB%03d", hotspot.param1), true))
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 4
+IMPLEMENT_ACTION(playMusic)
+ // Check that the file is not already buffered
+ Common::String filename = Common::String::format("MUS%03d", hotspot.param1);
+
+ if (!getSound()->isBuffered(filename) && (hotspot.param1 != 50 || getProgress().chapter == kChapter5))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 5
+IMPLEMENT_ACTION(knock)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(object).entity) {
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
+ } else {
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSoundEvent(kEntityPlayer, 12);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 6
+IMPLEMENT_ACTION(compartment)
+ ObjectIndex compartment = (ObjectIndex)hotspot.param1;
+
+ if (compartment >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(compartment).entity) {
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(compartment).entity, kActionOpenDoor, compartment);
+
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ if (handleOtherCompartment(compartment, true, true)) {
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ ObjectLocation location = getObjects()->get(compartment).location;
+ if (location == kObjectLocation1 || location == kObjectLocation3 || getEntities()->checkFields2(compartment)) {
+
+ if (location != kObjectLocation1 || getEntities()->checkFields2(compartment)
+ || (getInventory()->getSelectedItem() != kItemKey
+ && (compartment != kObjectCompartment1
+ || !getInventory()->hasItem(kItemKey)
+ || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))) {
+ if (!getSound()->isBuffered("LIB13"))
+ getSound()->playSoundEvent(kEntityPlayer, 13);
+
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ getSound()->playSoundEvent(kEntityPlayer, 32);
+
+ if ((compartment >= kObjectCompartment1 && compartment <= kObjectCompartment3) || (compartment >= kObjectCompartmentA && compartment <= kObjectCompartmentF))
+ getObjects()->update(compartment, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+ getInventory()->unselectItem();
+
+ return kSceneInvalid;
+ }
+
+ if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->getSelectedItem() != kItemKey) {
+ if (compartment == kObjectCageMax) {
+ getSound()->playSoundEvent(kEntityPlayer, 26);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+ }
+ return kSceneInvalid;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getSound()->playSoundEvent(kEntityPlayer, 16);
+ getInventory()->unselectItem();
+
+ // Stop processing further
+ return kSceneNone;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 7
+IMPLEMENT_ACTION(playSounds)
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1);
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param3, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 8
+IMPLEMENT_ACTION(playAnimation)
+ if (getEvent(hotspot.param1))
+ return kSceneInvalid;
+
+ playAnimation((EventIndex)hotspot.param1);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 9
+IMPLEMENT_ACTION(openCloseObject)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ getObjects()->update(object, getObjects()->get(object).entity, location, kCursorKeepValue, kCursorKeepValue);
+
+ bool isNotWindow = ((object <= kObjectCompartment8 || object >= kObjectHandleBathroom) && (object <= kObjectCompartmentH || object >= kObject48));
+
+ switch (location) {
+ default:
+ break;
+
+ case kObjectLocation1:
+ if (isNotWindow)
+ getSound()->playSoundEvent(kEntityPlayer, 24);
+ else
+ getSound()->playSoundEvent(kEntityPlayer, 21);
+ break;
+
+ case kObjectLocation2:
+ if (isNotWindow)
+ getSound()->playSoundEvent(kEntityPlayer, 36);
+ else
+ getSound()->playSoundEvent(kEntityPlayer, 20);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 10
+IMPLEMENT_ACTION(updateObjetLocation2)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ getObjects()->updateLocation2(object, location);
+
+ if (object != kObject112 || getSound()->isBuffered("LIB096")) {
+ if (object == 1)
+ getSound()->playSoundEvent(kEntityPlayer, 73);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 96);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 11
+IMPLEMENT_ACTION(setItemLocation)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (entry->isPresent)
+ return kSceneInvalid;
+
+ entry->location = (ObjectLocation)hotspot.param2;
+
+ if (item == kItemCorpse) {
+ ObjectLocation corpseLocation = getInventory()->get(kItemCorpse)->location;
+ getProgress().eventCorpseMovedFromFloor = (corpseLocation == kObjectLocation3 || corpseLocation == kObjectLocation4);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 12
+IMPLEMENT_ACTION(knockNoSound)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(object).entity)
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 13
+IMPLEMENT_ACTION(pickItem)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+ bool process = (hotspot.scene == 0);
+ SceneIndex sceneIndex = kSceneInvalid;
+
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (!entry->location)
+ return kSceneInvalid;
+
+ // Special case for corpse
+ if (item == kItemCorpse) {
+ pickCorpse(location, process);
+ return kSceneInvalid;
+ }
+
+ // Add and process items
+ getInventory()->addItem(item);
+
+ switch (item) {
+ default:
+ break;
+
+ case kItemGreenJacket:
+ pickGreenJacket(process);
+ break;
+
+ case kItemScarf:
+ pickScarf(process);
+
+ // stop processing
+ return kSceneInvalid;
+
+ case kItemParchemin:
+ if (location != kObjectLocation2)
+ break;
+
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getSound()->playSoundEvent(kEntityPlayer, 9);
+ break;
+
+ case kItemBomb:
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_pickBomb);
+ break;
+
+ case kItemBriefcase:
+ getSound()->playSoundEvent(kEntityPlayer, 83);
+ break;
+ }
+
+ // Load item scene
+ if (getInventory()->get(item)->scene) {
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneUseBackup = true;
+ getState()->sceneBackup = (hotspot.scene ? hotspot.scene : getState()->scene);
+ }
+
+ getScenes()->loadScene(getInventory()->get(item)->scene);
+
+ // do not process further
+ sceneIndex = kSceneNone;
+ }
+
+ // Select item
+ if (getInventory()->get(item)->isSelectable) {
+ getInventory()->selectItem(item);
+ _engine->getCursor()->setStyle(getInventory()->get(item)->cursor);
+ }
+
+ return sceneIndex;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 14
+IMPLEMENT_ACTION(dropItem)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+ bool process = (hotspot.scene == kSceneNone);
+
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ if (!getInventory()->hasItem(item))
+ return kSceneInvalid;
+
+ if (location < kObjectLocation1)
+ return kSceneInvalid;
+
+ // Handle actions
+ if (item == kItemBriefcase) {
+ getSound()->playSoundEvent(kEntityPlayer, 82);
+
+ if (location == kObjectLocation2) {
+ if (!getProgress().field_58) {
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getProgress().field_58 = 1;
+ }
+
+ if (getInventory()->get(kItemParchemin)->location == kObjectLocation2) {
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getSound()->playSoundEvent(kEntityPlayer, 9);
+ }
+ }
+ }
+
+ // Update item location
+ getInventory()->removeItem(item, location);
+
+ if (item == kItemCorpse)
+ dropCorpse(process);
+
+ // Unselect item
+ getInventory()->unselectItem();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 15: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 16
+IMPLEMENT_ACTION(enterCompartment)
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1 || getObjects()->get(kObjectCompartment1).location == kObjectLocation3 || getInventory()->getSelectedItem() == kItemKey)
+ return action_compartment(hotspot);
+
+ if (getProgress().eventCorpseFound) {
+ if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->get(kItemBriefcase)->location != kObjectLocation2)
+ return action_compartment(hotspot);
+
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+
+ if (getProgress().field_78 && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_78 = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 77);
+
+ return kSceneNone;
+ }
+
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ playAnimation(kEventCathFindCorpse);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getProgress().eventCorpseFound = true;
+
+ return kSceneCompartmentCorpse;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 17: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 18
+IMPLEMENT_ACTION(getOutsideTrain)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+
+ if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1)
+ && getProgress().isTrainRunning
+ && (object != kObjectOutsideAnnaCompartment || (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == kObjectLocation2))
+ && getInventory()->getSelectedItem() != kItemFirebird
+ && getInventory()->getSelectedItem() != kItemBriefcase) {
+
+ switch (object) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGoOutsideTylerCompartmentNight : kEventCathGoOutsideTylerCompartmentDay);
+ getProgress().field_C8 = 1;
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGoOutsideNight : kEventCathGoOutsideDay);
+ getProgress().field_C8 = 1;
+ break;
+
+ case kObjectOutsideAnnaCompartment:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
+ if (!hotspot.scene)
+ getScenes()->processScene();
+ break;
+ }
+ } else {
+ if (object == kObjectOutsideTylerCompartment || object == kObjectOutsideBetweenCompartments || object == kObjectOutsideAnnaCompartment) {
+ playAnimation(isNight() ? kEventCathLookOutsideWindowNight : kEventCathLookOutsideWindowDay);
+ getScenes()->processScene();
+ return kSceneNone;
+ }
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 19
+IMPLEMENT_ACTION(slip)
+ switch((ObjectIndex)hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ playAnimation(isNight() ? kEventCathSlipTylerCompartmentNight : kEventCathSlipTylerCompartmentDay);
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ playAnimation(isNight() ? kEventCathSlipNight : kEventCathSlipDay);
+ break;
+ }
+
+ getProgress().field_C8 = 0;
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 20
+IMPLEMENT_ACTION(getInsideTrain)
+ switch ((ObjectIndex)hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ playAnimation(isNight() ? kEventCathGetInsideTylerCompartmentNight : kEventCathGetInsideTylerCompartmentDay);
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
+ break;
+
+ case kObjectOutsideAnnaCompartment:
+ playAnimation(kEventCathGettingInsideAnnaCompartment);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 21
+IMPLEMENT_ACTION(climbUpTrain)
+ byte action = hotspot.param1;
+
+ if (action != 1 && action != 2)
+ return kSceneInvalid;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (action == 2)
+ playAnimation(kEventCathClimbUpTrainGreenJacket);
+
+ playAnimation(kEventCathTopTrainGreenJacket);
+ break;
+
+ case kChapter5:
+ if (action == 2)
+ playAnimation(getProgress().isNightTime ? kEventCathClimbUpTrainNoJacketNight : kEventCathClimbUpTrainNoJacketDay);
+
+ playAnimation(getProgress().isNightTime ? kEventCathTopTrainNoJacketNight : kEventCathTopTrainNoJacketDay);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 22
+IMPLEMENT_ACTION(climbDownTrain)
+ EventIndex evt = kEventNone;
+ switch (getProgress().chapter) {
+ default:
+ return kSceneInvalid;
+
+ case kChapter2:
+ case kChapter3:
+ evt = kEventCathClimbDownTrainGreenJacket;
+ break;
+
+ case kChapter5:
+ evt = (getProgress().isNightTime ? kEventCathClimbDownTrainNoJacketNight : kEventCathClimbDownTrainNoJacketDay);
+ break;
+ }
+
+ playAnimation(evt);
+ if (evt == kEventCathClimbDownTrainNoJacketDay)
+ getSound()->playSoundEvent(kEntityPlayer, 37);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 23
+IMPLEMENT_ACTION(jumpUpDownTrain)
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionBreakCeiling);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionJumpDownCeiling);
+ break;
+
+ case 3:
+ if (getInventory()->getSelectedItem() == kItemBriefcase) {
+ getInventory()->removeItem(kItemBriefcase, kObjectLocation3);
+ getSound()->playSoundEvent(kEntityPlayer, 82);
+ getInventory()->unselectItem();
+ }
+
+ // Show animation with or without briefcase
+ playAnimation((getInventory()->get(kItemBriefcase)->location - 3) ? kEventCathJumpUpCeilingBriefcase : kEventCathJumpUpCeiling);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ break;
+
+ case 4:
+ if (getProgress().chapter == kChapter1)
+ getSavePoints()->push(kEntityPlayer, kEntityKronos, kAction202621266);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 24
+IMPLEMENT_ACTION(unbound)
+ byte action = hotspot.param1;
+
+ switch (action) {
+ default:
+ break;
+
+ case 1:
+ playAnimation(kEventCathStruggleWithBonds);
+ if (hotspot.scene)
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ playAnimation(kEventCathBurnRope);
+ if (hotspot.scene)
+ getScenes()->processScene();
+ break;
+
+ case 3:
+ if (getEvent(kEventCathBurnRope)) {
+ playAnimation(kEventCathRemoveBonds);
+ getProgress().field_84 = 0;
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 89);
+ return kSceneNone;
+ }
+ break;
+
+ case 4:
+ if (!getEvent(kEventCathStruggleWithBonds2)) {
+ playAnimation(kEventCathStruggleWithBonds2);
+ getSound()->playSoundEvent(kEntityPlayer, 101);
+ getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation2);
+ if (!hotspot.scene)
+ getScenes()->processScene();
+ }
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityIvo, kAction192637492);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 25
+IMPLEMENT_ACTION(25)
+ switch(hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction272177921);
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered("MUS021"))
+ getSound()->playSound(kEntityPlayer, "MUS021", SoundManager::kFlagDefault);
+ break;
+
+ case 3:
+ getSound()->playSoundEvent(kEntityPlayer, 43);
+ if (!getInventory()->hasItem(kItemKey) && !getEvent(kEventAnnaBaggageArgument)) {
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_baggage);
+ return kSceneNone;
+ }
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 26
+IMPLEMENT_ACTION(26)
+ switch(hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction158610240);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225367984);
+ getInventory()->unselectItem();
+ return kSceneNone;
+
+ case 3:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction191001984);
+ return kSceneNone;
+
+ case 4:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction201959744);
+ return kSceneNone;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction169300225);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 27
+IMPLEMENT_ACTION(27)
+ if (!getSound()->isBuffered("LIB031", true))
+ getSound()->playSoundEvent(kEntityPlayer, 31);
+
+ switch (getEntityData(kEntityPlayer)->car) {
+ default:
+ break;
+
+ case kCarGreenSleeping:
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction225358684, hotspot.param1);
+ break;
+
+ case kCarRedSleeping:
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction225358684, hotspot.param1);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 28
+IMPLEMENT_ACTION(concertSitCough)
+ switch(hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case 1:
+ playAnimation(kEventConcertSit);
+ break;
+
+ case 2:
+ playAnimation(kEventConcertCough);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 29
+IMPLEMENT_ACTION(29)
+ getProgress().field_C = 1;
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
+
+ Common::String filename = Common::String::format("MUS%03d", hotspot.param3);
+ if (!getSound()->isBuffered(filename))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 30
+IMPLEMENT_ACTION(catchBeetle)
+ if (!getBeetle()->isLoaded())
+ return kSceneInvalid;
+
+ if (getBeetle()->catchBeetle()) {
+ getBeetle()->unload();
+ getInventory()->get(kItemBeetle)->location = kObjectLocation1;
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionCatchBeetle);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 31
+IMPLEMENT_ACTION(exitCompartment)
+ if (!getProgress().field_30 && getProgress().jacket != kJacketOriginal) {
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getProgress().field_30 = 1;
+ }
+
+ getObjects()->updateLocation2(kObjectCompartment1, (ObjectLocation)hotspot.param2);
+
+ // fall to case enterCompartment action
+ return action_enterCompartment(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 32
+IMPLEMENT_ACTION(32)
+ switch(hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntitySalko, kAction167992577);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction202884544);
+ break;
+
+ case 3:
+ if (getProgress().chapter == kChapter5) {
+ getSavePoints()->push(kEntityPlayer, kEntityAbbot, kAction168646401);
+ getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction168646401);
+ } else {
+ getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction203339360);
+ }
+ // Stop processing further scenes
+ return kSceneNone;
+
+ case 4:
+ getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction169773228);
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction167992577);
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityPlayer, kEntityAugust, kAction203078272);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 33
+IMPLEMENT_ACTION(useWhistle)
+ EventIndex evt = kEventNone;
+ SceneIndex sceneIndex = kSceneInvalid;
+
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction205294778);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) {
+ evt = kEventCathOpenEgg;
+
+ Scene *scene = getScenes()->get(hotspot.scene);
+ if (scene->getHotspot())
+ sceneIndex = scene->getHotspot()->scene;
+
+ } else {
+ evt = kEventCathOpenEggNoBackground;
+ }
+ getProgress().isEggOpen = true;
+ break;
+
+ case 2:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction224309120);
+ break;
+ }
+
+ evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathCloseEgg : kEventCathCloseEggNoBackground;
+ getProgress().isEggOpen = false;
+ break;
+
+ case 3:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kActionUseWhistle);
+ break;
+ }
+
+ evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathUseWhistleOpenEgg : kEventCathUseWhistleOpenEggNoBackground;
+ break;
+
+ }
+
+ if (evt != kEventNone) {
+ playAnimation(evt);
+ if (sceneIndex == kSceneNone || !hotspot.scene)
+ getScenes()->processScene();
+ }
+
+ return sceneIndex;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 34
+IMPLEMENT_ACTION(openMatchBox)
+ // If the match is already in the inventory, do nothing
+ if (!getInventory()->get(kItemMatch)->location
+ || getInventory()->get(kItemMatch)->isPresent)
+ return kSceneInvalid;
+
+ getInventory()->addItem(kItemMatch);
+ getSound()->playSoundEvent(kEntityPlayer, 102);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 35
+IMPLEMENT_ACTION(openBed)
+ getSound()->playSoundEvent(kEntityPlayer, 59);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 36: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 37
+IMPLEMENT_ACTION(dialog)
+ getSound()->playDialog(kEntityTables4, (EntityIndex)hotspot.param1, SoundManager::kFlagDefault, 0);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 38
+IMPLEMENT_ACTION(eggBox)
+ getSound()->playSoundEvent(kEntityPlayer, 43);
+ if (getProgress().field_7C && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_7C = 0;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 39
+IMPLEMENT_ACTION(39)
+ getSound()->playSoundEvent(kEntityPlayer, 24);
+ if (getProgress().field_80 && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_80 = 0;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 40
+IMPLEMENT_ACTION(bed)
+ getSound()->playSoundEvent(kEntityPlayer, 85);
+ // falls to case knockNoSound
+ return action_knockNoSound(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 41
+IMPLEMENT_ACTION(playMusicChapter)
+ byte id = 0;
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ id = hotspot.param1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ id = hotspot.param2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ id = hotspot.param3;
+ break;
+ }
+
+ if (id) {
+ Common::String filename = Common::String::format("MUS%03d", id);
+
+ if (!getSound()->isBuffered(filename))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 42
+IMPLEMENT_ACTION(playMusicChapterSetupTrain)
+ int id = 0;
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ id = 1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ id = 2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ id = 4;
+ break;
+ }
+
+ Common::String filename = Common::String::format("MUS%03d", hotspot.param1);
+
+ if (!getSound()->isBuffered(filename) && hotspot.param3 & id) {
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+
+ getSavePoints()->call(kEntityPlayer, kEntityTrain, kAction203863200, filename.c_str());
+ getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction222746496, hotspot.param2);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// // Action 43
+IMPLEMENT_ACTION(switchChapter)
+ // Nothing to do here as an hotspot action
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 44
+IMPLEMENT_ACTION(44)
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityRebecca, kAction205034665);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225358684);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helper functions
+//////////////////////////////////////////////////////////////////////////
+void Action::pickGreenJacket(bool process) const {
+ getProgress().jacket = kJacketGreen;
+ getInventory()->addItem(kItemMatchBox);
+
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ playAnimation(kEventPickGreenJacket);
+
+ getInventory()->setPortrait(kPortraitGreen);
+
+ if (process)
+ getScenes()->processScene();
+}
+
+void Action::pickScarf(bool process) const {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventPickScarfGreen : kEventPickScarfOriginal);
+
+ if (process)
+ getScenes()->processScene();
+}
+
+void Action::pickCorpse(ObjectLocation bedPosition, bool process) const {
+
+ if (getProgress().jacket == kJacketOriginal)
+ getProgress().jacket = kJacketBlood;
+
+ switch(getInventory()->get(kItemCorpse)->location) {
+ // No way to pick the corpse
+ default:
+ break;
+
+ // Floor
+ case kObjectLocation1:
+ // Bed is fully opened, move corpse directly there
+ if (bedPosition == 4) {
+ playAnimation(kEventCorpsePickFloorOpenedBedOriginal);
+
+ getInventory()->get(kItemCorpse)->location = kObjectLocation5;
+ break;
+ }
+
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickFloorGreen : kEventCorpsePickFloorOriginal);
+ break;
+
+ // Bed
+ case kObjectLocation2:
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickBedGreen : kEventCorpsePickBedOriginal);
+ break;
+ }
+
+ if (process)
+ getScenes()->processScene();
+
+ // Add corpse to inventory
+ if (bedPosition != 4) { // bed is not fully opened
+ getInventory()->addItem(kItemCorpse);
+ getInventory()->selectItem(kItemCorpse);
+ _engine->getCursor()->setStyle(kCursorCorpse);
+ }
+}
+
+void Action::dropCorpse(bool process) const {
+ switch(getInventory()->get(kItemCorpse)->location) {
+ default:
+ break;
+
+ case kObjectLocation1: // Floor
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropFloorGreen : kEventCorpseDropFloorOriginal);
+ break;
+
+ case kObjectLocation2: // Bed
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropBedGreen : kEventCorpseDropBedOriginal);
+ break;
+
+ case kObjectLocation4: // Window
+ // Say goodbye to an old friend
+ getInventory()->get(kItemCorpse)->location = kObjectLocationNone;
+ getProgress().eventCorpseThrown = true;
+
+ if (getState()->time <= kTime1138500) {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropWindowGreen : kEventCorpseDropWindowOriginal);
+
+ getProgress().field_24 = true;
+ } else {
+ playAnimation(kEventCorpseDropBridge);
+ }
+
+ getProgress().eventCorpseMovedFromFloor = true;
+ break;
+ }
+
+ if (process)
+ getScenes()->processScene();
+}
+
+bool Action::handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const {
+#define ENTITY_PARAMS(entity, index, id) \
+ ((EntityData::EntityParametersIIII*)getEntities()->get(entity)->getParamData()->getParameters(8, index))->param##id
+
+ // Only handle compartments
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideCompartment
+ || ((object < kObjectCompartment2 || object > kObjectCompartment8) && (object < kObjectCompartmentA || object > kObjectCompartmentH)))
+ return false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Gendarmes
+ if (getEntityData(kEntityPlayer)->car == getEntityData(kEntityGendarmes)->car
+ && getEntityData(kEntityGendarmes)->location == kLocationOutsideCompartment
+ && !getEntities()->compare(kEntityPlayer, kEntityGendarmes)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Mertens
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping
+ && getEntityData(kEntityMertens)->car == kCarGreenSleeping
+ && !getEntityData(kEntityMertens)->location
+ && !ENTITY_PARAMS(kEntityMertens, 0, 1)) {
+
+ if (!getEntities()->compare(kEntityPlayer, kEntityMertens)) {
+
+ if (getEntityData(kEntityMertens)->entityPosition < kPosition_2740
+ && getEntityData(kEntityMertens)->entityPosition > kPosition_850
+ && (getEntityData(kEntityCoudert)->car != kCarGreenSleeping || getEntityData(kEntityCoudert)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityVerges)->car != kCarGreenSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playWarningCompartment(kEntityMertens, object);
+
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction305159806);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ if (getEntityData(kEntityMertens)->direction == kDirectionUp
+ && getEntityData(kEntityMertens)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+ }
+
+ if (getEntityData(kEntityMertens)->direction == kDirectionDown
+ && getEntityData(kEntityMertens)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object, true);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Coudert
+ if (getEntityData(kEntityPlayer)->car != kCarRedSleeping
+ || !getEntityData(kEntityCoudert)->car
+ || getEntityData(kEntityCoudert)->location != kLocationOutsideCompartment
+ || ENTITY_PARAMS(kEntityCoudert, 0, 1))
+ return false;
+
+ if (!getEntities()->compare(kEntityPlayer, kEntityCoudert)) {
+
+ if (getEntityData(kEntityCoudert)->entityPosition < kPosition_2740
+ && getEntityData(kEntityCoudert)->entityPosition > kPosition_850
+ && (getEntityData(kEntityMertens)->car != kCarRedSleeping || getEntityData(kEntityMertens)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityVerges)->car != kCarRedSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityMmeBoutarel)->car != kCarRedSleeping || getEntityData(kEntityMmeBoutarel)->entityPosition > kPosition_2740)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playWarningCompartment(kEntityCoudert, object);
+
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction305159806);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ // Direction = Up
+ if (getEntityData(kEntityCoudert)->direction == kDirectionUp
+ && getEntityData(kEntityCoudert)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ // Direction = down
+ if (getEntityData(kEntityCoudert)->direction == kDirectionDown
+ && getEntityData(kEntityCoudert)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object, true);
+ }
+ }
+
+ return false;
+}
+
+void Action::playCompartmentSoundEvents(ObjectIndex object) const {
+ if (getObjects()->get(object).location == kObjectLocation1 || getObjects()->get(object).location == kObjectLocation3 || getEntities()->checkFields2(object)) {
+ getSound()->playSoundEvent(kEntityPlayer, 13);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 3);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Cursors
+//////////////////////////////////////////////////////////////////////////
+CursorStyle Action::getCursor(const SceneHotspot &hotspot) const {
+ // Simple cursor style
+ if (hotspot.cursor != kCursorProcess)
+ return (CursorStyle)hotspot.cursor;
+
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+
+ switch (hotspot.action) {
+ default:
+ return kCursorNormal;
+
+ case SceneHotspot::kActionInventory:
+ if (!getState()->sceneBackup2 && (getEvent(kEventKronosBringFirebird) || getProgress().isEggOpen))
+ return kCursorNormal;
+ else
+ return kCursorBackward;
+
+ case SceneHotspot::kActionKnockOnDoor:
+ warning("================================= DOOR %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+ else
+ return (CursorStyle)getObjects()->get(object).cursor;
+
+ case SceneHotspot::kAction12:
+ warning("================================= OBJECT %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getObjects()->get(object).entity)
+ return (CursorStyle)getObjects()->get(object).cursor;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionPickItem:
+ warning("================================= ITEM %03d =================================", object);
+ if (object >= kObjectCompartmentA)
+ return kCursorNormal;
+
+ if ((!getInventory()->getSelectedItem() || getInventory()->getSelectedEntry()->manualSelect)
+ && (object != kObject21 || getProgress().eventCorpseMovedFromFloor))
+ return kCursorHand;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionDropItem:
+ warning("================================= ITEM %03d =================================", object);
+ if (object >= kObjectCompartmentA)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() != (InventoryItem)object)
+ return kCursorNormal;
+
+ if (object == kObject20 && hotspot.param2 == 4 && !getProgress().isTrainRunning)
+ return kCursorNormal;
+
+ if (object == kObjectHandleInsideBathroom && hotspot.param2 == 1 && getProgress().field_5C)
+ return kCursorNormal;
+
+ return (CursorStyle)getInventory()->getSelectedEntry()->cursor;
+
+ case SceneHotspot::kAction15:
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getProgress().isEqual(object, hotspot.param2))
+ return (CursorStyle)hotspot.param3;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionEnterCompartment:
+ if ((getInventory()->getSelectedItem() != kItemKey || getObjects()->get(kObjectCompartment1).location)
+ && (getObjects()->get(kObjectCompartment1).location != 1 || !getInventory()->hasItem(kItemKey)
+ || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))
+ goto LABEL_KEY;
+
+ return (CursorStyle)getInventory()->get(kItemKey)->cursor; // TODO is that always the same as kCursorKey ?
+
+ case SceneHotspot::kActionGetOutsideTrain:
+ if (getProgress().jacket != kJacketGreen)
+ return kCursorNormal;
+
+ if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowDay) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1)
+ && getProgress().isTrainRunning
+ && (object != kObjectOutsideAnnaCompartment || (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == 2))
+ && getInventory()->getSelectedItem() != kItemBriefcase && getInventory()->getSelectedItem() != kItemFirebird)
+ return kCursorForward;
+
+ return (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation2) ? kCursorNormal : kCursorMagnifier;
+
+ case SceneHotspot::kActionSlip:
+ return (getProgress().field_C8 < 1) ? kCursorNormal : kCursorLeft;
+
+ case SceneHotspot::kActionClimbUpTrain:
+ if (getProgress().isTrainRunning
+ && (getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3 || getProgress().chapter == kChapter5)
+ && getInventory()->getSelectedItem() != kItemFirebird
+ && getInventory()->getSelectedItem() != kItemBriefcase)
+ return kCursorUp;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionJumpUpDownTrain:
+ if (object != kObjectCompartment1)
+ return kCursorNormal;
+
+ return (getObjects()->get(kObjectCeiling).location < kObjectLocation1) ? kCursorHand : kCursorNormal;
+
+ case SceneHotspot::kActionUnbound:
+ if (hotspot.param2 != 2)
+ return kCursorNormal;
+
+ if (getEvent(kEventCathBurnRope) || !getEvent(kEventCathStruggleWithBonds2))
+ return kCursorNormal;
+
+ return kCursorHand;
+
+ case SceneHotspot::kActionCatchBeetle:
+ if (!getBeetle()->isLoaded())
+ return kCursorNormal;
+
+ if (!getBeetle()->isCatchable())
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() == kItemMatchBox && getInventory()->hasItem(kItemMatch))
+ return (CursorStyle)getInventory()->get(kItemMatchBox)->cursor;
+
+ return kCursorHandPointer;
+
+ case SceneHotspot::KActionUseWhistle:
+ if (object != kObjectCompartment3)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() == kItemWhistle)
+ return kCursorWhistle;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionOpenBed:
+ if (getProgress().chapter < kChapter2)
+ return kCursorHand;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionDialog:
+ if (getSound()->getDialogName((EntityIndex)object))
+ return kCursorHandPointer;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionBed:
+ if (getProgress().field_18 == 2 && !getProgress().field_E4
+ && (getState()->time > kTimeBedTime
+ || (getProgress().eventMetAugust && getProgress().field_CC
+ && (!getProgress().field_24 || getProgress().field_3C))))
+ return kCursorSleep;
+
+ return kCursorNormal;
+
+LABEL_KEY:
+ // Handle compartment action
+ case SceneHotspot::kActionCompartment:
+ case SceneHotspot::kActionExitCompartment:
+ warning("================================= DOOR %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() != kItemKey
+ || getObjects()->get(object).entity
+ || getObjects()->get(object).location != 1
+ || !getObjects()->get(object).cursor2
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields2(object))
+ return (CursorStyle)getObjects()->get(object).cursor2;
+ else
+ return (CursorStyle)getInventory()->get(kItemKey)->cursor;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Animation
+//////////////////////////////////////////////////////////////////////////
+
+// Play an animation and add delta time to global game time
+void Action::playAnimation(EventIndex index, bool debugMode) const {
+ if (index >= _animationListSize)
+ error("Action::playAnimation: invalid event index (value=%i, max=%i)", index, _animationListSize);
+
+ // In debug mode, just show the animation
+ if (debugMode) {
+ Animation animation;
+ if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis")))
+ animation.play();
+ return;
+ }
+
+ getFlags()->flag_3 = true;
+
+ // Hide cursor
+ _engine->getCursor()->show(false);
+
+ // Show inventory & hourglass
+ getInventory()->show();
+ getInventory()->showHourGlass();
+
+ if (!getFlags()->mouseRightClick) {
+
+ if (getGlobalTimer()) {
+ if (getSound()->isBuffered("TIMER")) {
+ getSound()->processEntry("TIMER");
+ setGlobalTimer(105);
+ }
+ }
+
+ bool processSound = false;
+ if (index >= kEventCorpseDropFloorOriginal
+ || index == kEventCathWakingUp
+ || index == kEventConcertCough
+ || index == kEventConcertSit
+ || index == kEventConcertLeaveWithBriefcase)
+ processSound = true;
+
+ Animation animation;
+ if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis") , processSound ? Animation::kFlagDefault : Animation::kFlagProcess))
+ animation.play();
+
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+ }
+
+ // Show cursor
+ _engine->getCursor()->show(true);
+
+ getEvent(index) = 1;
+
+ // Adjust game time
+ getState()->timeTicks += _animationList[index].time;
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)(_animationList[index].time * getState()->timeDelta));
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/action.h b/engines/lastexpress/game/action.h
new file mode 100644
index 0000000000..6a2e3eb597
--- /dev/null
+++ b/engines/lastexpress/game/action.h
@@ -0,0 +1,135 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ACTION_H
+#define LASTEXPRESS_ACTION_H
+
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/func.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+#define DECLARE_ACTION(name) \
+ SceneIndex action_##name(const SceneHotspot &hotspot) const
+
+#define ADD_ACTION(name) \
+ _actions.push_back(new Functor1MemConst<const SceneHotspot &, SceneIndex, Action>(this, &Action::action_##name));
+
+#define IMPLEMENT_ACTION(name) \
+ SceneIndex Action::action_##name(const SceneHotspot &hotspot) const { \
+ debugC(6, kLastExpressDebugLogic, "Hotspot action: " #name "%s", hotspot.toString().c_str());
+
+class LastExpressEngine;
+class SceneHotspot;
+
+class Action {
+public:
+ Action(LastExpressEngine *engine);
+ ~Action();
+
+ // Hotspot action
+ SceneIndex processHotspot(const SceneHotspot &hotspot);
+
+ // Cursor
+ CursorStyle getCursor(const SceneHotspot &hotspot) const;
+
+ // Animation
+ void playAnimation(EventIndex index, bool debugMode = false) const;
+
+ // Compartment action
+ bool handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const;
+
+private:
+ typedef Common::Functor1<const SceneHotspot &, SceneIndex> ActionFunctor;
+
+ LastExpressEngine *_engine;
+ Common::Array<ActionFunctor *> _actions;
+
+ // Each action is of the form action_<name>(SceneHotspot *hotspot)
+ // - a pointer to each action is added to the _actions array
+ // - processHotspot simply calls the proper function given by the hotspot->action value
+ //
+ // Note: even though there are 44 actions, only 41 are used in processHotspot
+
+ DECLARE_ACTION(inventory);
+ DECLARE_ACTION(savePoint);
+ DECLARE_ACTION(playSound);
+ DECLARE_ACTION(playMusic);
+ DECLARE_ACTION(knock);
+ DECLARE_ACTION(compartment);
+ DECLARE_ACTION(playSounds);
+ DECLARE_ACTION(playAnimation);
+ DECLARE_ACTION(openCloseObject);
+ DECLARE_ACTION(updateObjetLocation2);
+ DECLARE_ACTION(setItemLocation);
+ DECLARE_ACTION(knockNoSound);
+ DECLARE_ACTION(pickItem);
+ DECLARE_ACTION(dropItem);
+ DECLARE_ACTION(enterCompartment);
+ DECLARE_ACTION(getOutsideTrain);
+ DECLARE_ACTION(slip);
+ DECLARE_ACTION(getInsideTrain);
+ DECLARE_ACTION(climbUpTrain);
+ DECLARE_ACTION(climbDownTrain);
+ DECLARE_ACTION(jumpUpDownTrain);
+ DECLARE_ACTION(unbound);
+ DECLARE_ACTION(25);
+ DECLARE_ACTION(26);
+ DECLARE_ACTION(27);
+ DECLARE_ACTION(concertSitCough);
+ DECLARE_ACTION(29);
+ DECLARE_ACTION(catchBeetle);
+ DECLARE_ACTION(exitCompartment);
+ DECLARE_ACTION(32);
+ DECLARE_ACTION(useWhistle);
+ DECLARE_ACTION(openMatchBox);
+ DECLARE_ACTION(openBed);
+ DECLARE_ACTION(dialog);
+ DECLARE_ACTION(eggBox);
+ DECLARE_ACTION(39);
+ DECLARE_ACTION(bed);
+ DECLARE_ACTION(playMusicChapter);
+ DECLARE_ACTION(playMusicChapterSetupTrain);
+ DECLARE_ACTION(switchChapter);
+ DECLARE_ACTION(44);
+
+ // Special dummy function
+ DECLARE_ACTION(dummy);
+
+ // Helpers
+ void pickGreenJacket(bool process) const;
+ void pickScarf(bool process) const;
+ void pickCorpse(ObjectLocation bedPosition, bool process) const;
+ void dropCorpse(bool process) const;
+
+ void playCompartmentSoundEvents(ObjectIndex object) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ACTION_H
diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp
new file mode 100644
index 0000000000..665edb79a5
--- /dev/null
+++ b/engines/lastexpress/game/beetle.cpp
@@ -0,0 +1,517 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/beetle.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+Beetle::Beetle(LastExpressEngine *engine) : _engine(engine), _data(NULL) {}
+
+Beetle::~Beetle() {
+ SAFE_DELETE(_data);
+
+ // Free passed pointers
+ _engine = NULL;
+}
+
+void Beetle::load() {
+ // Only load in chapter 2 & 3
+ if (getProgress().chapter != kChapter2 && getProgress().chapter != kChapter3)
+ return;
+
+ // Already loaded
+ if (_data)
+ return;
+
+ // Do not load if beetle is in the wrong location
+ if (getInventory()->get(kItemBeetle)->location != kObjectLocation3)
+ return;
+
+ ///////////////////////
+ // Load Beetle data
+ _data = new BeetleData();
+
+ // Load sequences
+ _data->sequences.push_back(loadSequence("BW000.seq")); // 0
+ _data->sequences.push_back(loadSequence("BT000045.seq"));
+ _data->sequences.push_back(loadSequence("BT045000.seq"));
+ _data->sequences.push_back(loadSequence("BW045.seq"));
+ _data->sequences.push_back(loadSequence("BT045090.seq"));
+ _data->sequences.push_back(loadSequence("BT090045.seq")); // 5
+ _data->sequences.push_back(loadSequence("BW090.seq"));
+ _data->sequences.push_back(loadSequence("BT090135.seq"));
+ _data->sequences.push_back(loadSequence("BT135090.seq"));
+ _data->sequences.push_back(loadSequence("BW135.seq"));
+ _data->sequences.push_back(loadSequence("BT135180.seq")); // 10
+ _data->sequences.push_back(loadSequence("BT180135.seq"));
+ _data->sequences.push_back(loadSequence("BW180.seq"));
+ _data->sequences.push_back(loadSequence("BT180225.seq"));
+ _data->sequences.push_back(loadSequence("BT225180.seq"));
+ _data->sequences.push_back(loadSequence("BW225.seq")); // 15
+ _data->sequences.push_back(loadSequence("BT225270.seq"));
+ _data->sequences.push_back(loadSequence("BT270225.seq"));
+ _data->sequences.push_back(loadSequence("BW270.seq"));
+ _data->sequences.push_back(loadSequence("BT270315.seq"));
+ _data->sequences.push_back(loadSequence("BT315270.seq")); // 20
+ _data->sequences.push_back(loadSequence("BW315.seq"));
+ _data->sequences.push_back(loadSequence("BT315000.seq"));
+ _data->sequences.push_back(loadSequence("BT000315.seq"));
+ _data->sequences.push_back(loadSequence("BA135.seq"));
+ _data->sequences.push_back(loadSequence("BL045.seq")); // 25
+ _data->sequences.push_back(loadSequence("BL000.seq"));
+ _data->sequences.push_back(loadSequence("BL315.seq"));
+ _data->sequences.push_back(loadSequence("BL180.seq"));
+
+ // Init fields
+ _data->field_74 = 0;
+
+ // Check that all sequences are loaded properly
+ _data->isLoaded = true;
+ for (int i = 0; i < (int)_data->sequences.size(); i++) {
+ if (!_data->sequences[i]->isLoaded()) {
+ _data->isLoaded = false;
+ break;
+ }
+ }
+
+ _data->field_D9 = 10;
+ _data->coordOffset = 5;
+ _data->coordY = 178;
+ _data->currentSequence = 0;
+ _data->offset = 0;
+ _data->frame = NULL;
+ _data->field_D5 = 0;
+ _data->indexes[0] = 29;
+ _data->field_DD = 0;
+}
+
+void Beetle::unload() {
+ // Remove sequences from display list
+ if (_data)
+ getScenes()->removeFromQueue(_data->frame);
+
+ // Delete all loaded sequences
+ SAFE_DELETE(_data);
+}
+
+bool Beetle::isLoaded() const {
+ if (!_data)
+ return false;
+
+ return _data->isLoaded;
+}
+
+bool Beetle::catchBeetle() {
+ if (!_data)
+ error("Beetle::catchBeetle: sequences have not been loaded!");
+
+ if (getInventory()->getSelectedItem() == kItemMatchBox
+ && getInventory()->hasItem(kItemMatch)
+ && ABS((int16)(getCoords().x - _data->coordX)) < 10
+ && ABS((int16)(getCoords().y - _data->coordY)) < 10) {
+ return true;
+ }
+
+ _data->field_D5 = 0;
+ move();
+
+ return false;
+}
+
+bool Beetle::isCatchable() const {
+ if (!_data)
+ error("Beetle::isCatchable: sequences have not been loaded!");
+
+ return (_data->indexes[_data->offset] >= 30);
+}
+
+void Beetle::update() {
+ if (!_data)
+ error("Beetle::update: sequences have not been loaded!");
+
+ if (!_data->isLoaded)
+ return;
+
+ move();
+
+ if (_data->field_D5)
+ _data->field_D5--;
+
+ if (_data->currentSequence && _data->indexes[_data->offset] != 29) {
+ drawUpdate();
+ return;
+ }
+
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ if ((!_data->field_DD && rnd(10) < 1)
+ || (_data->field_DD && rnd(30) < 1)
+ || rnd(100) < 1) {
+
+ _data->field_DD++;
+ if (_data->field_DD > 3)
+ _data->field_DD = 0;
+
+ updateData(24);
+
+ _data->coordX = (int16)(rnd(250) + 190);
+ _data->coordOffset = (int16)(rnd(5) + 5);
+
+ if (_data->field_D9 > 1)
+ _data->field_D9--;
+
+ drawUpdate();
+ }
+ }
+}
+
+void Beetle::drawUpdate() {
+ if (!_data)
+ error("Beetle::drawUpdate: sequences have not been loaded!");
+
+ if (_data->frame != NULL) {
+ getScenes()->setCoordinates(_data->frame);
+ getScenes()->removeFromQueue(_data->frame);
+ }
+
+ // Update current frame
+ switch (_data->indexes[_data->offset]) {
+ default:
+ _data->currentFrame += 10;
+ break;
+
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ _data->currentFrame++;
+ break;
+ }
+
+ // Update current sequence
+ if (_data->currentSequence->count() <= _data->currentFrame) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ _data->offset++;
+ _data->currentSequence = _data->sequences[_data->indexes[_data->offset]];
+ break;
+
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ break;
+ }
+
+ _data->currentFrame = 0;
+ if (_data->indexes[_data->offset] == 29) {
+ SAFE_DELETE(_data->frame);
+ _data->currentSequence = NULL; // pointer to existing sequence
+ return;
+ }
+ }
+
+ // Update coordinates
+ switch (_data->indexes[_data->offset]) {
+ default:
+ break;
+
+ case 0:
+ _data->coordY -= _data->coordOffset;
+ break;
+
+ case 3:
+ _data->coordX += _data->coordOffset;
+ _data->coordY -= _data->coordOffset;
+ break;
+
+ case 6:
+ _data->coordX += _data->coordOffset;
+ break;
+
+ case 9:
+ _data->coordX += _data->coordOffset;
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 12:
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 15:
+ _data->coordX -= _data->coordOffset;
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 18:
+ _data->coordX -= _data->coordOffset;
+ break;
+
+ case 21:
+ _data->coordX -= _data->coordOffset;
+ _data->coordY -= _data->coordOffset;
+ break;
+ }
+
+ // Update beetle data
+ int rnd = rnd(100);
+ if (_data->coordX < 165 || _data->coordX > 465) {
+ uint index = 0;
+
+ if (rnd >= 30) {
+ if (rnd >= 70)
+ index = (_data->coordX < 165) ? 9 : 15;
+ else
+ index = (_data->coordX < 165) ? 6 : 18;
+ } else {
+ index = (_data->coordX < 165) ? 3 : 21;
+ }
+
+ updateData(index);
+ }
+
+ if (_data->coordY < 178) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ updateData(26);
+ break;
+
+ case 3:
+ updateData(25);
+ break;
+
+ case 21:
+ updateData(27);
+ break;
+ }
+ }
+
+ if (_data->coordY > 354) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ break;
+
+ case 9:
+ case 12:
+ case 15:
+ updateData(28);
+ break;
+ }
+ }
+
+#define INVERT_Y() \
+ switch (_data->indexes[_data->offset]) { \
+ default: \
+ break; \
+ case 24: \
+ case 25: \
+ case 26: \
+ case 27: \
+ case 28: \
+ _data->coordY = -_data->coordY; \
+ break; \
+ }
+
+ // Invert direction
+ INVERT_Y();
+
+ SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame);
+ updateFrame(frame);
+
+ INVERT_Y();
+
+ getScenes()->addToQueue(frame);
+
+ SAFE_DELETE(_data->frame);
+ _data->frame = frame;
+}
+
+void Beetle::move() {
+ if (!_data)
+ error("Beetle::move: sequences have not been loaded!");
+
+ if (_data->indexes[_data->offset] >= 24 && _data->indexes[_data->offset] <= 29)
+ return;
+
+ if (_data->field_D5)
+ return;
+
+ if (ABS((int)(getCoords().x - _data->coordX)) > 35)
+ return;
+
+ if (ABS((int)(getCoords().y - _data->coordY)) > 35)
+ return;
+
+ int32 deltaX = getCoords().x - _data->coordX;
+ int32 deltaY = -getCoords().y - _data->coordY;
+ uint32 index = 0;
+
+ // FIXME: check code path
+ if (deltaX >= 0) {
+ if (deltaY > 0) {
+ if (100 * deltaY - 241 * deltaX <= 0) {
+ if (100 * deltaY - 41 * deltaX <= 0)
+ index = 18;
+ else
+ index = 15;
+ } else {
+ index = 12;
+ }
+
+ goto update_data;
+ }
+ }
+
+ if (deltaX < 0) {
+
+ if (deltaY > 0) {
+ if (100 * deltaY + 241 * deltaX <= 0) {
+ if (100 * deltaY + 41 * deltaX <= 0)
+ index = 6;
+ else
+ index = 9;
+ } else {
+ index = 12;
+ }
+
+ goto update_data;
+ }
+
+ if (deltaY <= 0) {
+ if (100 * deltaY - 41 * deltaX <= 0) {
+ if (100 * deltaY - 241 * deltaX <= 0)
+ index = 0;
+ else
+ index = 3;
+ } else {
+ index = 6;
+ }
+
+ goto update_data;
+ }
+ }
+
+update_data:
+ updateData(index);
+
+ if (_data->coordOffset >= 15) {
+ _data->field_D5 = 0;
+ return;
+ }
+
+ _data->coordOffset = _data->coordOffset + (int16)(4 * rnd(100)/100 + _data->field_D9);
+ _data->field_D5 = 0;
+}
+
+// Update the beetle sequence to show the correct frames in the correct place
+void Beetle::updateFrame(SequenceFrame *frame) const {
+ if (!_data)
+ error("Beetle::updateSequence: sequences have not been loaded!");
+
+ if (!frame)
+ return;
+
+ // Update coordinates
+ if (_data->coordX > 0)
+ frame->getInfo()->xPos1 = (uint16)_data->coordX;
+
+ if (_data->coordY > 0)
+ frame->getInfo()->yPos1 = (uint16)_data->coordY;
+}
+
+void Beetle::updateData(uint32 index) {
+ if (!_data)
+ error("Beetle::updateData: sequences have not been loaded!");
+
+ if (!_data->isLoaded)
+ return;
+
+ if (index == 25 || index == 26 || index == 27 || index == 28) {
+ _data->indexes[0] = index;
+ _data->indexes[1] = 29;
+ _data->offset = 0;
+
+ _data->currentSequence = _data->sequences[index];
+ _data->currentFrame = 0;
+ _data->index = index;
+ } else {
+ if (!_data->sequences[index])
+ return;
+
+ if (_data->index == index)
+ return;
+
+ _data->offset = 0;
+
+ // Special case for sequence 24
+ if (index == 24) {
+ _data->indexes[0] = index;
+ _data->coordY = 178;
+ _data->index = _data->indexes[1];
+ _data->indexes[1] = (_data->coordX >= 265) ? 15 : 9;
+ _data->currentFrame = 0;
+ _data->currentSequence = _data->sequences[index];
+ } else {
+ if (index <= _data->index) {
+ for (uint32 i = _data->index - 1; i > index; ++_data->offset) {
+ _data->indexes[_data->offset] = i;
+ i -= 3;
+ }
+ } else {
+ for (uint32 i = _data->index + 1; i < index; ++_data->offset) {
+ _data->indexes[_data->offset] = i;
+ i += 3;
+ }
+ }
+
+ _data->index = index;
+ _data->indexes[_data->offset] = index;
+ _data->currentFrame = 0;
+ _data->offset = 0;
+ _data->currentSequence = _data->sequences[_data->indexes[0]];
+ }
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/beetle.h b/engines/lastexpress/game/beetle.h
new file mode 100644
index 0000000000..661618a251
--- /dev/null
+++ b/engines/lastexpress/game/beetle.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_BEETLE_H
+#define LASTEXPRESS_BEETLE_H
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/helpers.h"
+
+#include "common/array.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Beetle {
+public:
+
+ Beetle(LastExpressEngine *engine);
+ ~Beetle();
+
+ void update();
+
+ void load();
+ void unload();
+
+ bool isLoaded() const;
+
+ bool catchBeetle();
+ bool isCatchable() const;
+
+private:
+ struct BeetleData {
+ Common::Array<Sequence *> sequences;
+
+ uint32 field_74;
+ Sequence *currentSequence;
+ uint32 currentFrame;
+ uint32 index;
+ int16 coordOffset;
+ int16 field_86;
+
+ int16 coordX;
+ int16 coordY;
+
+ uint32 indexes[16];
+
+ uint32 offset;
+ SequenceFrame *frame;
+ bool isLoaded;
+ uint32 field_D5;
+ uint32 field_D9;
+ uint32 field_DD;
+
+ BeetleData() {
+ field_74 = 0;
+ currentSequence = NULL;
+ currentFrame = 0;
+ index = 0;
+ coordOffset = 0;
+
+ field_86 = 0;
+
+ coordX = 0;
+ coordY = 0;
+
+ memset(indexes, 0, sizeof(indexes));
+ offset = 0;
+
+ frame = NULL;
+ isLoaded = false;
+ field_D5 = 0;
+ field_D9 = 0;
+ field_DD = 0;
+ }
+
+ ~BeetleData() {
+ for (int i = 0; i < (int)sequences.size(); i++)
+ SAFE_DELETE(sequences[i]);
+
+ sequences.clear();
+ }
+ };
+
+ LastExpressEngine *_engine;
+
+ BeetleData *_data;
+
+ void move();
+ void updateFrame(SequenceFrame *frame) const;
+ void updateData(uint32 index);
+ void drawUpdate();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BEETLE_H
diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp
new file mode 100644
index 0000000000..d124d8cb23
--- /dev/null
+++ b/engines/lastexpress/game/entities.cpp
@@ -0,0 +1,2748 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/entities.h"
+
+// Data
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+
+// Entities
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/alouan.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/boutarel.h"
+#include "lastexpress/entities/chapters.h"
+#include "lastexpress/entities/cooks.h"
+#include "lastexpress/entities/coudert.h"
+#include "lastexpress/entities/entity39.h"
+#include "lastexpress/entities/francois.h"
+#include "lastexpress/entities/gendarmes.h"
+#include "lastexpress/entities/hadija.h"
+#include "lastexpress/entities/ivo.h"
+#include "lastexpress/entities/kahina.h"
+#include "lastexpress/entities/kronos.h"
+#include "lastexpress/entities/mahmud.h"
+#include "lastexpress/entities/max.h"
+#include "lastexpress/entities/mertens.h"
+#include "lastexpress/entities/milos.h"
+#include "lastexpress/entities/mmeboutarel.h"
+#include "lastexpress/entities/pascale.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/servers0.h"
+#include "lastexpress/entities/servers1.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tables.h"
+#include "lastexpress/entities/tatiana.h"
+#include "lastexpress/entities/train.h"
+#include "lastexpress/entities/vassili.h"
+#include "lastexpress/entities/verges.h"
+#include "lastexpress/entities/vesna.h"
+#include "lastexpress/entities/yasmin.h"
+
+// Game
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+#define STORE_VALUE(data) ((uint)1 << (uint)data)
+
+static const EntityPosition objectsPosition[8] = {kPosition_8200, kPosition_7500,
+ kPosition_6470, kPosition_5790,
+ kPosition_4840, kPosition_4070,
+ kPosition_3050, kPosition_2740};
+
+static const EntityPosition entityPositions[41] = {
+ kPositionNone, kPosition_851, kPosition_1430, kPosition_2110, kPositionNone,
+ kPosition_2410, kPosition_2980, kPosition_3450, kPosition_3760, kPosition_4100,
+ kPosition_4680, kPosition_5140, kPosition_5440, kPosition_5810, kPosition_6410,
+ kPosition_6850, kPosition_7160, kPosition_7510, kPosition_8514, kPositionNone,
+ kPositionNone, kPositionNone, kPosition_2086, kPosition_2690, kPositionNone,
+ kPosition_3110, kPosition_3390, kPosition_3890, kPosition_4460, kPosition_4770,
+ kPosition_5090, kPosition_5610, kPosition_6160, kPosition_6460, kPosition_6800,
+ kPosition_7320, kPosition_7870, kPosition_8160, kPosition_8500, kPosition_9020,
+ kPosition_9269};
+
+#define ADD_ENTITY(class) \
+ _entities.push_back(new class(engine));
+
+#define COMPUTE_SEQUENCE_NAME(sequenceTo, sequenceFrom) { \
+ sequenceTo = sequenceFrom; \
+ for (int seqIdx = 0; seqIdx < 7; seqIdx++) \
+ sequenceTo.deleteLastChar(); \
+ if (isInsideTrainCar(entityIndex, kCarGreenSleeping) || isInsideTrainCar(entityIndex, kCarGreenSleeping)) { \
+ if (data->car < getData(kEntityPlayer)->car || (data->car == getData(kEntityPlayer)->car && data->entityPosition < getData(kEntityPlayer)->entityPosition)) \
+ sequenceTo += "R.SEQ"; \
+ else \
+ sequenceTo += "F.SEQ"; \
+ } else { \
+ sequenceTo += ".SEQ"; \
+ } \
+}
+
+#define TRY_LOAD_SEQUENCE(sequence, name, name1, name2) { \
+ if (data->car == getData(kEntityPlayer)->car) \
+ sequence = loadSequence1(name1, field30); \
+ if (sequence) { \
+ name = name1; \
+ } else { \
+ if (name2 != "") \
+ sequence = loadSequence1(name2, field30); \
+ name = (sequence ? name2 : ""); \
+ } \
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entities
+//////////////////////////////////////////////////////////////////////////
+Entities::Entities(LastExpressEngine *engine) : _engine(engine) {
+ _header = new EntityData();
+
+ _entities.push_back(NULL); // Header
+ ADD_ENTITY(Anna);
+ ADD_ENTITY(August);
+ ADD_ENTITY(Mertens);
+ ADD_ENTITY(Coudert);
+ ADD_ENTITY(Pascale);
+ ADD_ENTITY(Servers0);
+ ADD_ENTITY(Servers1);
+ ADD_ENTITY(Cooks);
+ ADD_ENTITY(Verges);
+ ADD_ENTITY(Tatiana);
+ ADD_ENTITY(Vassili);
+ ADD_ENTITY(Alexei);
+ ADD_ENTITY(Abbot);
+ ADD_ENTITY(Milos);
+ ADD_ENTITY(Vesna);
+ ADD_ENTITY(Ivo);
+ ADD_ENTITY(Salko);
+ ADD_ENTITY(Kronos);
+ ADD_ENTITY(Kahina);
+ ADD_ENTITY(Francois);
+ ADD_ENTITY(MmeBoutarel);
+ ADD_ENTITY(Boutarel);
+ ADD_ENTITY(Rebecca);
+ ADD_ENTITY(Sophie);
+ ADD_ENTITY(Mahmud);
+ ADD_ENTITY(Yasmin);
+ ADD_ENTITY(Hadija);
+ ADD_ENTITY(Alouan);
+ ADD_ENTITY(Gendarmes);
+ ADD_ENTITY(Max);
+ ADD_ENTITY(Chapters);
+ ADD_ENTITY(Train);
+
+ // Special case for tables
+ _entities.push_back(new Tables(engine, kEntityTables0));
+ _entities.push_back(new Tables(engine, kEntityTables1));
+ _entities.push_back(new Tables(engine, kEntityTables2));
+ _entities.push_back(new Tables(engine, kEntityTables3));
+ _entities.push_back(new Tables(engine, kEntityTables4));
+ _entities.push_back(new Tables(engine, kEntityTables5));
+
+ ADD_ENTITY(Entity39);
+
+ // Init compartments & positions
+ memset(&_compartments, 0, sizeof(_compartments));
+ memset(&_compartments1, 0, sizeof(_compartments1));
+ memset(&_positions, 0, sizeof(_positions));
+}
+
+Entities::~Entities() {
+ SAFE_DELETE(_header);
+
+ for (int i = 0; i < (int)_entities.size(); i++)
+ SAFE_DELETE(_entities[i]);
+
+ _entities.clear();
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Accessors
+//////////////////////////////////////////////////////////////////////////
+Entity *Entities::get(EntityIndex entity) {
+ assert((uint)entity < _entities.size());
+
+ if (entity == kEntityPlayer)
+ error("Cannot get entity for index == 0!");
+
+ return _entities[entity];
+}
+
+EntityData::EntityCallData *Entities::getData(EntityIndex entity) const {
+ assert((uint)entity < _entities.size());
+
+ if (entity == kEntityPlayer)
+ return _header->getCallData();
+
+ return _entities[entity]->getData();
+}
+
+int Entities::getPosition(CarIndex car, Position position) const {
+ int index = 100 * car + position;
+
+ if (car > 10)
+ error("Entities::getPosition: trying to access an invalid car (was: %d, valid:0-9)", car);
+
+ if (position > 100)
+ error("Entities::getPosition: trying to access an invalid position (was: %d, valid:0-100)", position);
+
+ return _positions[index];
+}
+
+int Entities::getCompartments(int index) const {
+ if (index >= _compartmentsCount)
+ error("Entities::getCompartments: trying to access an invalid compartment (was: %d, valid:0-15)", index);
+
+ return _compartments[index];
+}
+
+int Entities::getCompartments1(int index) const {
+ if (index >= _compartmentsCount)
+ error("Entities::getCompartments: trying to access an invalid compartment (was: %d, valid:0-15)", index);
+
+ return _compartments1[index];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame
+//////////////////////////////////////////////////////////////////////////
+void Entities::saveLoadWithSerializer(Common::Serializer &s) {
+ _header->saveLoadWithSerializer(s);
+ for (uint i = 1; i < _entities.size(); i++)
+ _entities[i]->saveLoadWithSerializer(s);
+}
+
+void Entities::savePositions(Common::Serializer &s) {
+ for (uint i = 0; i < (uint)_positionsCount; i++)
+ s.syncAsUint32LE(_positions[i]);
+}
+
+void Entities::saveCompartments(Common::Serializer &s) {
+ for (uint i = 0; i < (uint)_compartmentsCount; i++)
+ s.syncAsUint32LE(_compartments[i]);
+
+ for (uint i = 0; i < (uint)_compartmentsCount; i++)
+ s.syncAsUint32LE(_compartments1[i]);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+void Entities::setup(bool isFirstChapter, EntityIndex entityIndex) {
+ setupChapter(isFirstChapter ? kChapter1 : kChapterAll);
+
+ bool flag_4 = false;
+
+ if (!isFirstChapter) {
+ getFlags()->flag_4 = false;
+
+ if (entityIndex) {
+ getSavePoints()->call(kEntityPlayer, entityIndex, kActionNone);
+ flag_4 = getFlags()->flag_4;
+ }
+ }
+
+ getFlags()->flag_4 = flag_4;
+ if (!getFlags()->flag_4)
+ getScenes()->loadScene(getState()->scene);
+}
+
+void Entities::setupChapter(ChapterIndex chapter) {
+ if (chapter) {
+ // Reset current call, inventory item & draw sequences
+ for (uint i = 1; i < _entities.size(); i++) {
+ getData((EntityIndex)i)->currentCall = 0;
+ getData((EntityIndex)i)->inventoryItem = kItemNone;
+
+ clearSequences((EntityIndex)i);
+ }
+
+ // Init compartments & positions
+ memset(&_compartments, 0, sizeof(_compartments));
+ memset(&_compartments1, 0, sizeof(_compartments1));
+ memset(&_positions, 0, sizeof(_positions));
+
+ getSound()->resetQueue(SoundManager::kSoundType13);
+ }
+
+ // we skip the header when doing entity setup
+ for (uint i = 1; i < _entities.size(); i++) {
+ // Special case of chapters (prevents infinite loop as we will be called from Chapters functions when changing chapters)
+ if (i == kEntityChapters && chapter >= 2)
+ continue;
+
+ _entities[i]->setup(chapter);
+ }
+}
+
+void Entities::reset() {
+ // Reset header
+ delete _header;
+ _header = new EntityData();
+
+ for (uint i = 1; i < _entities.size(); i++)
+ resetSequences((EntityIndex)i);
+
+ getScenes()->resetDoorsAndClock();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// State & Sequences
+//////////////////////////////////////////////////////////////////////////
+
+EntityIndex Entities::canInteractWith(const Common::Point &point) const {
+ if (!getFlags()->isGameRunning)
+ return kEntityPlayer;
+
+ EntityIndex index = kEntityPlayer;
+ int location = 10000;
+
+ // Check if there is an entity we can interact with
+ for (uint i = 0; i < _entities.size(); i++) {
+
+ // Skip entities with no current frame
+ if (!getData((EntityIndex)i)->frame)
+ continue;
+
+ FrameInfo *info = getData((EntityIndex)i)->frame->getInfo();
+
+ // Check the hotspot
+ if (info->hotspot.contains(point)) {
+
+ // If closer to us, update with its values
+ if (location > info->location) {
+ location = info->location;
+ index = (EntityIndex)i;
+ }
+ }
+ }
+
+ // Check if we found an entity
+ if (!index)
+ return kEntityPlayer;
+
+ // Check that there is an item to interact with
+ if (!getData(index)->inventoryItem)
+ return kEntityPlayer;
+
+ return index;
+}
+
+void Entities::resetState(EntityIndex entityIndex) {
+ getData(entityIndex)->currentCall = 0;
+ getData(entityIndex)->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(entityIndex))
+ getSound()->removeFromQueue(entityIndex);
+
+ clearSequences(entityIndex);
+
+ if (entityIndex == kEntity39)
+ entityIndex = kEntityPlayer;
+
+ if (entityIndex > kEntityChapters)
+ return;
+
+ // reset compartments and positions for this entity
+ for (int i = 0; i < _positionsCount; i++)
+ _positions[i] &= ~STORE_VALUE(entityIndex);
+
+ for (int i = 0; i < _compartmentsCount; i++) {
+ _compartments[i] &= ~STORE_VALUE(entityIndex);
+ _compartments1[i] &= ~STORE_VALUE(entityIndex);
+ }
+
+ getLogic()->updateCursor();
+}
+
+
+void Entities::updateFields() const {
+ if (!getFlags()->isGameRunning)
+ return;
+
+ for (int i = 0; i < (int)_entities.size(); i++) {
+
+ if (!getSavePoints()->getCallback((EntityIndex)i))
+ continue;
+
+ EntityData::EntityCallData *data = getData((EntityIndex)i);
+ int positionDelta = data->field_4A3 * 10;
+ switch (data->direction) {
+ default:
+ break;
+
+ case kDirectionUp:
+ if (data->entityPosition >= 10000 - positionDelta)
+ data->entityPosition = (EntityPosition)(data->entityPosition + positionDelta);
+ break;
+
+ case kDirectionDown:
+ if (data->entityPosition > positionDelta)
+ data->entityPosition = (EntityPosition)(data->entityPosition - positionDelta);
+ break;
+
+ case kDirectionLeft:
+ data->currentFrame++;
+ break;
+
+ case kDirectionRight:
+ data->field_4A1 += 9;
+ break;
+
+ case kDirectionSwitch:
+ if (data->directionSwitch == kDirectionRight)
+ data->field_4A1 += 9;
+ break;
+
+ }
+ }
+}
+
+void Entities::updateFrame(EntityIndex entityIndex) const {
+ Sequence *sequence = NULL;
+ int16 *currentFrame = NULL;
+ bool found = false;
+
+ if (getData(entityIndex)->direction == kDirectionSwitch) {
+ sequence = getData(entityIndex)->sequence2;
+ currentFrame = &getData(entityIndex)->currentFrame2;
+ } else {
+ sequence = getData(entityIndex)->sequence;
+ currentFrame = &getData(entityIndex)->currentFrame;
+ }
+
+ if (!sequence)
+ return;
+
+ // Save current values
+ int16 oldFrame = *currentFrame;
+ int16 field_4A1 = getData(entityIndex)->field_4A1;
+
+ do {
+ // Check we do not get past the end
+ if (*currentFrame >= (int)sequence->count() - 1)
+ break;
+
+ // Get the proper frame
+ FrameInfo *info = sequence->getFrameInfo((uint16)*currentFrame);
+
+ if (info->field_33 & 8) {
+ found = true;
+ } else {
+ if (info->soundAction == 35)
+ found = true;
+
+ getData(entityIndex)->field_4A1 += info->field_30;
+
+ // Progress to the next frame
+ ++*currentFrame;
+ }
+ } while (!found);
+
+ // Restore old values
+ if (!found) {
+ *currentFrame = oldFrame;
+ getData(entityIndex)->field_4A1 = field_4A1;
+ }
+}
+
+void Entities::updateSequences() const {
+ if (!getFlags()->isGameRunning)
+ return;
+
+ // Update the train clock & doors
+ getScenes()->updateDoorsAndClock();
+
+ //////////////////////////////////////////////////////////////////////////
+ // First pass: Drawing
+ //////////////////////////////////////////////////////////////////////////
+ for (uint i = 1; i < _entities.size(); i++) {
+ EntityIndex entityIndex = (EntityIndex)i;
+
+ if (!getSavePoints()->getCallback(entityIndex))
+ continue;
+
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->frame) {
+ getScenes()->removeFromQueue(data->frame);
+ SAFE_DELETE(data->frame);
+ }
+
+ if (data->frame1) {
+ getScenes()->removeFromQueue(data->frame1);
+ SAFE_DELETE(data->frame1);
+ }
+
+ if (data->direction == kDirectionSwitch) {
+
+ // Clear sequence 2
+ if (data->sequence)
+ SAFE_DELETE(data->sequence);
+
+ // Replace by sequence 3 if available
+ if (data->sequence2) {
+ data->sequence = data->sequence2;
+ data->sequenceName = data->sequenceName2;
+
+ data->sequence2 = NULL;
+ data->sequenceName2 = "";
+ }
+
+ data->direction = data->directionSwitch;
+ data->currentFrame = -1;
+ data->field_49B = 0;
+ }
+
+ // Draw sequences
+ drawSequences(entityIndex, data->direction, false);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Second pass: Load sequences for next pass
+ //////////////////////////////////////////////////////////////////////////
+ for (uint i = 1; i < _entities.size(); i++) {
+ EntityIndex entityIndex = (EntityIndex)i;
+
+ if (!getSavePoints()->getCallback(entityIndex))
+ continue;
+
+ EntityData::EntityCallData *data = getData(entityIndex);
+ byte field30 = (data->direction == kDirectionLeft ? entityIndex + 35 : 15);
+
+ if (data->sequenceName != "" && !data->sequence) {
+ data->sequence = loadSequence1(data->sequenceName, field30);
+
+ // If sequence 2 was loaded correctly, remove the copied name
+ // otherwise, compute new name
+ if (data->sequence) {
+ data->sequenceNameCopy = "";
+ } else {
+ Common::String sequenceName;
+
+ // Left and down directions
+ if (data->direction == kDirectionLeft || data->direction == kDirectionRight) {
+ COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName);
+
+ // Try loading the sequence
+ data->sequence = loadSequence1(sequenceName, field30);
+ }
+
+ // Update sequence names
+ data->sequenceNameCopy = (data->sequence ? "" : data->sequenceName);
+ data->sequenceName = (data->sequence ? sequenceName : "");
+ }
+ }
+
+ // Update sequence 3
+ if (data->sequenceName2 != "" && !data->sequence2) {
+
+ if (data->car == getData(kEntityPlayer)->car)
+ data->sequence2 = loadSequence1(data->sequenceName2, field30);
+
+ if (!data->sequence2) {
+ Common::String sequenceName;
+
+ // Left and down directions
+ if (data->directionSwitch == kDirectionLeft || data->directionSwitch == kDirectionRight) {
+ COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName2);
+
+ // Try loading the sequence
+ data->sequence2 = loadSequence1(sequenceName, field30);
+ }
+
+ // Update sequence names
+ data->sequenceName2 = (data->sequence2 ? sequenceName : "");
+ }
+ }
+ }
+}
+
+void Entities::resetSequences(EntityIndex entityIndex) const {
+
+ // Reset direction
+ if (getData(entityIndex)->direction == kDirectionSwitch) {
+ getData(entityIndex)->direction = getData(entityIndex)->directionSwitch;
+ getData(entityIndex)->field_49B = 0;
+ getData(entityIndex)->currentFrame = -1;
+ }
+
+ SAFE_DELETE(getData(entityIndex)->frame);
+ SAFE_DELETE(getData(entityIndex)->frame1);
+
+ SAFE_DELETE(getData(entityIndex)->sequence);
+ SAFE_DELETE(getData(entityIndex)->sequence2);
+ SAFE_DELETE(getData(entityIndex)->sequence3);
+
+ getData(entityIndex)->field_4A9 = false;
+ getData(entityIndex)->field_4AA = false;
+
+ strcpy((char*)&getData(entityIndex)->sequenceNameCopy, "");
+ strcpy((char*)&getData(entityIndex)->sequenceName, "");
+ strcpy((char*)&getData(entityIndex)->sequenceName2, "");
+
+ getScenes()->resetQueue();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+//////////////////////////////////////////////////////////////////////////
+void Entities::updateCallbacks() {
+ if (!getFlags()->isGameRunning)
+ return;
+
+ getFlags()->flag_entities_0 = false;
+
+ if (getFlags()->flag_entities_1) {
+ executeCallbacks();
+ getFlags()->flag_entities_0 = true;
+ } else {
+ getFlags()->flag_entities_1 = true;
+ executeCallbacks();
+ getFlags()->flag_entities_1 = false;
+ }
+}
+
+void Entities::executeCallbacks() {
+ for (uint i = 1; i < _entities.size(); i++) {
+ if (getFlags()->flag_entities_0)
+ break;
+
+ if (getSavePoints()->getCallback((EntityIndex)i))
+ processEntity((EntityIndex)i);
+ }
+
+ if (getFlags()->flag_entities_0)
+ return;
+
+ bool processed = true;
+ do {
+ processed = true;
+ for (int i = 1; i < (int)_entities.size(); i++) {
+ if (getFlags()->flag_entities_0)
+ break;
+
+ if (getSavePoints()->getCallback((EntityIndex)i)) {
+ if (getData((EntityIndex)i)->doProcessEntity) {
+ processed = false;
+ processEntity((EntityIndex)i);
+ }
+ }
+ }
+ } while (!processed);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Processing
+//////////////////////////////////////////////////////////////////////////
+#define INCREMENT_DIRECTION_COUNTER() { \
+ data->doProcessEntity = false; \
+ if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) \
+ ++data->field_4A1; \
+ }
+
+void Entities::processEntity(EntityIndex entityIndex) {
+ EntityData::EntityCallData *data = getData(entityIndex);
+ bool keepPreviousFrame = false;
+
+ data->doProcessEntity = false;
+
+ if (getData(kEntityPlayer)->car != data->car && data->direction != kDirectionRight && data->direction != kDirectionSwitch) {
+
+ if (data->position) {
+ updatePositionExit(entityIndex, data->car2, data->position);
+ data->car2 = kCarNone;
+ data->position = 0;
+ }
+
+ getScenes()->removeAndRedraw(&data->frame, false);
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+
+ if (data->frame1) {
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ if (data->frame && data->frame->getInfo()->subType != kFrameType3) {
+ data->frame->getInfo()->subType = kFrameTypeNone;
+ getScenes()->setFlagDrawSequences();
+ }
+ }
+
+ SAFE_DELETE(data->sequence3);
+
+ if (!data->frame || !data->direction) {
+ if (!data->sequence)
+label_nosequence:
+ drawSequences(entityIndex, data->direction, true);
+
+ data->doProcessEntity = false;
+ computeCurrentFrame(entityIndex);
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+
+ if (data->sequence && data->currentFrame != -1 && data->currentFrame <= (int16)(data->sequence->count() - 1)) {
+ processFrame(entityIndex, false, true);
+
+ if (!getFlags()->flag_entities_0 && !data->doProcessEntity) {
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+ } else {
+ if (data->direction == kDirectionRight && data->field_4A1 > 100) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (data->position) {
+ updatePositionExit(entityIndex, data->car2, data->position);
+ data->car2 = kCarNone;
+ data->position = 0;
+ }
+
+ INCREMENT_DIRECTION_COUNTER();
+ }
+ return;
+ }
+
+ if (!data->sequence)
+ goto label_nosequence;
+
+ if (data->frame->getInfo()->field_30 > data->field_49B + 1 || (data->direction == kDirectionLeft && data->sequence->count() == 1)) {
+ ++data->field_49B;
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+
+ if (data->frame->getInfo()->field_30 > data->field_49B && !data->frame->getInfo()->keepPreviousFrame) {
+ ++data->field_49B;
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+
+ if (data->frame->getInfo()->keepPreviousFrame == 1)
+ keepPreviousFrame = true;
+
+ // Increment current frame
+ ++data->currentFrame;
+
+ if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) {
+
+ if (data->direction == kDirectionLeft) {
+ data->currentFrame = 0;
+ } else {
+ keepPreviousFrame = true;
+ drawNextSequence(entityIndex);
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+
+ if (!data->sequence2) {
+ updateEntityPosition(entityIndex);
+ data->doProcessEntity = false;
+ return;
+ }
+
+ copySequenceData(entityIndex);
+ }
+
+ }
+
+ processFrame(entityIndex, keepPreviousFrame, false);
+
+ if (!getFlags()->flag_entities_0 && !data->doProcessEntity)
+ INCREMENT_DIRECTION_COUNTER();
+}
+
+void Entities::computeCurrentFrame(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+ int16 originalCurrentFrame = data->currentFrame;
+
+ if (!data->sequence) {
+ data->currentFrame = -1;
+ return;
+ }
+
+ switch (data->direction) {
+ default:
+ break;
+
+ case kDirectionNone:
+ case kDirectionSwitch:
+ data->currentFrame = -1;
+ break;
+
+ case kDirectionUp:
+ case kDirectionDown: {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ if (scene->position > 40)
+ break;
+
+ switch (scene->position) {
+ default:
+ case 4:
+ case 19:
+ case 20:
+ case 21:
+ case 24:
+ break;
+
+ case 1:
+ case 18:
+ case 22:
+ case 40:
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
+ break;
+
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ if (data->field_4A9) {
+ if (getData(kEntityPlayer)->entityPosition >= data->entityPosition) {
+ data->currentFrame = -1;
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
+
+ if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
+ if (data->currentFrame < (int)(data->sequence->count() - 2))
+ data->currentFrame += 2;
+ }
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
+ }
+ break;
+
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ if (data->field_4A9) {
+ if (getData(kEntityPlayer)->entityPosition <= data->entityPosition) {
+ data->currentFrame = -1;
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
+
+ if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
+ if (data->currentFrame < (int)(data->sequence->count() - 2))
+ data->currentFrame += 2;
+ }
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
+ }
+ break;
+ }
+
+ }
+ break;
+
+
+ case kDirectionLeft:
+ if (data->currentFrame == -1 || data->currentFrame >= (int32)data->sequence->count()) {
+ data->currentFrame = 0;
+ data->field_49B = 0;
+ }
+ break;
+
+ case kDirectionRight:
+ bool found = false;
+ bool flag = false;
+ uint16 frameIndex = 0;
+ byte field30 = 0;
+
+ int16 currentFrameCopy = (!data->currentFrame && !data->field_4A1) ? -1 : data->currentFrame;
+
+ // Process frames
+ do {
+ if (frameIndex >= data->sequence->count())
+ break;
+
+ FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
+
+ if (field30 + info->field_30 >= data->field_4A1) {
+ found = true;
+ break;
+ }
+
+ if (field30 > data->field_4A1 - 10) {
+ if (info->soundAction)
+ getSound()->playSoundEvent(entityIndex, info->soundAction, (field30 <= data->field_4A1 - info->field_31) ? 0 : (byte)(field30 + info->field_31 - data->field_4A1));
+ }
+
+ field30 += info->field_30;
+
+ if (info->field_33 & 4)
+ flag = true;
+
+ if (info->field_33 & 2) {
+ flag = false;
+
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (info->field_33 & 16) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ frameIndex++;
+
+ } while (!found);
+
+ if (found) {
+
+ if (flag) {
+ bool found2 = false;
+
+ do {
+ if (frameIndex >= data->sequence->count())
+ break;
+
+ FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
+ if (info->field_33 & 2) {
+ found2 = true;
+
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+
+ } else {
+ data->field_4A1 += info->field_30;
+
+ byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
+ if (soundAction)
+ getSound()->playSoundEvent(entityIndex, soundAction);
+
+ ++frameIndex;
+ }
+
+ } while (!found2);
+
+ if (found2) {
+ data->currentFrame = frameIndex;
+ data->field_49B = 0;
+
+ byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
+ byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
+ if (soundAction && data->currentFrame != currentFrameCopy)
+ getSound()->playSoundEvent(entityIndex, soundAction, field31);
+
+ } else {
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
+ }
+
+ } else {
+
+ data->currentFrame = frameIndex;
+ data->field_49B = data->field_4A1 - field30;
+
+ byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
+ byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
+ if (soundAction && data->currentFrame != currentFrameCopy)
+ getSound()->playSoundEvent(entityIndex, soundAction, field31 <= data->field_49B ? 0 : (byte)(field31 - data->field_49B));
+ }
+ } else {
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
+
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
+ getSavePoints()->process();
+ }
+ break;
+ }
+}
+
+int16 Entities::getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const {
+ EntityData::EntityCallData *data = getData(entity);
+
+ EntityPosition firstFramePosition = sequence->getFrameInfo(0)->entityPosition;
+ EntityPosition lastFramePosition = sequence->getFrameInfo(sequence->count() - 1)->entityPosition;
+
+ bool isGoingForward = (firstFramePosition < lastFramePosition);
+
+ if (!doProcessing) {
+ if (!isGoingForward) {
+ if (data->field_4A3 + firstFramePosition < data->entityPosition || lastFramePosition - data->field_4A3 > data->entityPosition)
+ return -1;
+ } else {
+ if (firstFramePosition - data->field_4A3 > data->entityPosition || lastFramePosition + data->field_4A3 < data->entityPosition)
+ return -1;
+ }
+ }
+
+ if (sequence->count() == 0)
+ return 0;
+
+ // Search for the correct frame
+ // TODO: looks slightly like some sort of binary search
+ uint16 frame = 0;
+ uint16 numFrames = sequence->count() - 1;
+
+ for (;;) {
+ uint16 currentFrame = (frame + numFrames) / 2;
+
+ if (position + sequence->getFrameInfo(currentFrame)->entityPosition <= data->entityPosition) {
+ if (!isGoingForward)
+ numFrames = (frame + numFrames) / 2;
+ else
+ frame = (frame + numFrames) / 2;
+ } else {
+ if (isGoingForward)
+ numFrames = (frame + numFrames) / 2;
+ else
+ frame = (frame + numFrames) / 2;
+ }
+
+ if (numFrames - frame == 1) {
+ uint16 lastFramePos = ABS(position - (sequence->getFrameInfo(numFrames)->entityPosition + data->entityPosition));
+ uint16 framePosition = ABS(position - (sequence->getFrameInfo(frame)->entityPosition + data->entityPosition));
+
+ return (framePosition > lastFramePos) ? numFrames : frame;
+ }
+
+ if (numFrames <= frame)
+ return currentFrame;
+ }
+}
+
+void Entities::processFrame(EntityIndex entityIndex, bool keepPreviousFrame, bool dontPlaySound) {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ // Set frame to be drawn again
+ if (data->frame && keepPreviousFrame) {
+ if (data->frame->getInfo()->subType != kFrameType3)
+ data->frame->getInfo()->subType = kFrameType2;
+
+ getScenes()->setFlagDrawSequences();
+ }
+
+ // Remove old frame from queue
+ if (data->frame && !keepPreviousFrame)
+ getScenes()->removeFromQueue(data->frame);
+
+ // Stop if nothing else to draw
+ if (data->currentFrame < 0)
+ return;
+
+ if (data->currentFrame > (int)data->sequence->count())
+ return;
+
+ // Get new frame info
+ FrameInfo *info = data->sequence->getFrameInfo((uint16)data->currentFrame);
+
+ if (data->frame && data->frame->getInfo()->subType != kFrameType3)
+ if (!info->field_2E || keepPreviousFrame)
+ getScenes()->setCoordinates(data->frame);
+
+ // Update position
+ if (info->entityPosition) {
+ data->entityPosition = info->entityPosition;
+ if (data->field_4A9)
+ data->entityPosition = (EntityPosition)(data->entityPosition + getEntityPositionFromCurrentPosition());
+ }
+
+ info->location = entityIndex + ABS(getData(entityIndex)->entityPosition - getData(kEntityPlayer)->entityPosition);
+
+ if (info->subType != kFrameType3) {
+ info->subType = kFrameType1;
+
+ if (!keepPreviousFrame)
+ info->subType = kFrameTypeNone;
+ }
+
+ if (info->field_33 & 1)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExcuseMeCath);
+
+ if (info->field_33 & 2) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (info->field_33 & 16) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (data->position) {
+ updatePositionExit(entityIndex, data->car2, data->position);
+ data->car2 = kCarNone;
+ data->position = 0;
+ }
+
+ if (info->position) {
+ data->car2 = data->car;
+ data->position = info->position;
+ updatePositionEnter(entityIndex, data->car2, data->position);
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (info->soundAction && !dontPlaySound)
+ getSound()->playSoundEvent(entityIndex, info->soundAction, info->field_31);
+
+ // Add the new frame to the queue
+ SequenceFrame *frame = new SequenceFrame(data->sequence, (uint16)data->currentFrame);
+ getScenes()->addToQueue(frame);
+
+ // Keep previous frame if needed and store the new frame
+ if (keepPreviousFrame) {
+ SAFE_DELETE(data->frame1);
+ data->frame1 = data->frame;
+ } else {
+ SAFE_DELETE(data->frame);
+ }
+
+ data->frame = frame;
+
+ if (!dontPlaySound)
+ data->field_49B = keepPreviousFrame ? 0 : 1;
+}
+
+void Entities::drawNextSequence(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->direction == kDirectionRight) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (!isDirectionUpOrDown(entityIndex))
+ return;
+
+ if (data->sequence2)
+ return;
+
+ if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingAtDoors))
+ return;
+
+ if (getData(kEntityPlayer)->car != data->car)
+ return;
+
+ if (!data->field_4A9 || isWalkingOppositeToPlayer(entityIndex)) {
+ if (!data->field_4A9 && isWalkingOppositeToPlayer(entityIndex)) {
+ data->entityPosition = kPosition_2088;
+
+ if (data->direction != kDirectionUp)
+ data->entityPosition = kPosition_8512;
+
+ drawSequences(entityIndex, data->direction, true);
+ }
+ } else {
+ data->entityPosition = kPosition_8514;
+
+ if (data->direction != kDirectionUp)
+ data->entityPosition = kPosition_2086;
+
+ drawSequences(entityIndex, data->direction, true);
+ }
+}
+
+void Entities::updateEntityPosition(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ getScenes()->removeAndRedraw(&data->frame, false);
+
+ SAFE_DELETE(data->frame1);
+ data->field_49B = 0;
+
+ if (isDirectionUpOrDown(entityIndex)
+ && (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
+ && data->car == getData(kEntityPlayer)->car) {
+
+ if (isWalkingOppositeToPlayer(entityIndex)) {
+ data->entityPosition = getData(kEntityPlayer)->entityPosition;
+ } else if (data->field_4A9) {
+ data->entityPosition = (data->direction == kDirectionUp) ? kPosition_8514 : kPosition_2086;
+ } else {
+ if (isPlayerPosition(kCarGreenSleeping, 1) || isPlayerPosition(kCarGreenSleeping, 40)
+ || isPlayerPosition(kCarRedSleeping, 1) || isPlayerPosition(kCarRedSleeping, 40)) {
+ data->entityPosition = (data->direction == kDirectionUp) ? kPosition_2588 : kPosition_8012;
+ } else {
+ data->entityPosition = (data->direction == kDirectionUp) ? kPosition_9271 : kPosition_849;
+ }
+ }
+ }
+
+ SAFE_DELETE(data->sequence);
+ data->sequenceName = "";
+ data->field_4A9 = false;
+
+ if (data->directionSwitch)
+ data->direction = data->directionSwitch;
+}
+
+void Entities::copySequenceData(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->sequence)
+ data->sequence3 = data->sequence;
+
+ data->sequence = data->sequence2;
+ data->sequenceName = data->sequenceName2;
+ data->field_4A9 = data->field_4AA;
+
+ if (data->directionSwitch)
+ data->direction = data->directionSwitch;
+
+ // Clear sequence 3
+ data->sequence2 = NULL;
+ data->sequenceName2 = "";
+ data->field_4AA = false;
+ data->directionSwitch = kDirectionNone;
+
+ if (data->field_4A9) {
+ computeCurrentFrame(entityIndex);
+
+ if (data->currentFrame == -1)
+ data->currentFrame = 0;
+ } else {
+ data->currentFrame = data->currentFrame2;
+ data->currentFrame2 = 0;
+
+ if (data->currentFrame == -1)
+ data->currentFrame = 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////
+void Entities::drawSequenceLeft(EntityIndex index, const char *sequence) const {
+ drawSequence(index, sequence, kDirectionLeft);
+}
+
+void Entities::drawSequenceRight(EntityIndex index, const char *sequence) const {
+ drawSequence(index, sequence, kDirectionRight);
+}
+
+void Entities::clearSequences(EntityIndex entityIndex) const {
+ debugC(8, kLastExpressDebugLogic, "Clear sequences for entity %s", ENTITY_NAME(entityIndex));
+
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ getScenes()->removeAndRedraw(&data->frame, false);
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ if (data->sequence2) {
+ SAFE_DELETE(data->sequence2);
+ data->sequenceName2 = "";
+ data->field_4AA = false;
+ data->directionSwitch = kDirectionNone;
+ }
+
+ if (data->sequence) {
+ SAFE_DELETE(data->sequence);
+ data->sequenceName = "";
+ data->field_4A9 = false;
+ data->currentFrame = -1;
+ }
+
+ data->sequenceNamePrefix = "";
+ data->direction = kDirectionNone;
+ data->doProcessEntity = true;
+}
+
+void Entities::drawSequence(EntityIndex index, const char *sequence, EntityDirection direction) const {
+ debugC(8, kLastExpressDebugLogic, "Drawing sequence %s for entity %s with direction %s", sequence, ENTITY_NAME(index), DIRECTION_NAME(direction));
+
+ // Copy sequence name
+ getData(index)->sequenceNamePrefix = sequence;
+ getData(index)->sequenceNamePrefix.toUppercase();
+ getData(index)->sequenceNamePrefix += "-";
+
+ // Reset fields
+ getData(index)->field_49B = 0;
+ getData(index)->currentFrame = 0;
+ getData(index)->field_4A1 = 0;
+
+ drawSequences(index, direction, true);
+}
+
+void Entities::drawSequences(EntityIndex entityIndex, EntityDirection direction, bool loadSequence) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ // Compute value for loading sequence depending on direction
+ byte field30 = (direction == kDirectionLeft ? entityIndex + 35 : 15);
+
+ data->doProcessEntity = true;
+ bool field4A9 = data->field_4A9;
+
+ // First case: different car and not going right: cleanup and return
+ if (data->car != getData(kEntityPlayer)->car && direction != kDirectionRight) {
+ clearEntitySequenceData(data, direction);
+ return;
+ }
+
+ data->directionSwitch = kDirectionNone;
+
+ // Process sequence names
+ Common::String sequenceName;
+ Common::String sequenceName1;
+ Common::String sequenceName2;
+ Common::String sequenceName3;
+
+ getSequenceName(entityIndex, direction, sequenceName1, sequenceName2);
+
+ // No sequence 1: cleanup and return
+ if (sequenceName1 == "") {
+ clearEntitySequenceData(data, direction);
+ return;
+ }
+
+ if (sequenceName1 == data->sequenceNameCopy) {
+ data->direction = direction;
+ return;
+ }
+
+ if (direction == kDirectionLeft || direction == kDirectionRight) {
+ COMPUTE_SEQUENCE_NAME(sequenceName, sequenceName1);
+
+ if (sequenceName3 != "")
+ COMPUTE_SEQUENCE_NAME(sequenceName3, sequenceName2);
+ }
+
+ if (!data->frame) {
+ data->direction = direction;
+
+ if (sequenceName1 == data->sequenceName) {
+ if (sequenceName2 == "")
+ return;
+
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ SAFE_DELETE(data->sequence);
+
+ if (sequenceName1 != data->sequenceName2) {
+
+ if (loadSequence) {
+
+ if (data->car == getData(kEntityPlayer)->car)
+ data->sequence = loadSequence1(sequenceName1, field30);
+
+ if (data->sequence) {
+ data->sequenceName = sequenceName1;
+ data->sequenceNameCopy = "";
+ } else {
+ if (sequenceName != "")
+ data->sequence = loadSequence1(sequenceName, field30);
+
+ data->sequenceName = (data->sequence ? sequenceName : "");
+ data->sequenceNameCopy = (data->sequence ? "" : sequenceName1);
+ }
+ } else {
+ data->sequenceName = sequenceName1;
+ }
+
+ if (sequenceName2 != "") {
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ if (!data->sequence2) {
+ if (sequenceName2 == "")
+ return;
+
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ SAFE_DELETE(data->sequence2);
+ } else {
+ data->sequence = data->sequence2;
+ data->sequenceName = data->sequenceName2;
+ data->sequence2 = NULL;
+ }
+
+ data->sequenceName2 = "";
+
+ if (sequenceName2 == "")
+ return;
+
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ if (data->sequenceName != sequenceName1) {
+
+ if (data->sequenceName2 != sequenceName1) {
+ SAFE_DELETE(data->sequence2);
+ TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName1, sequenceName);
+ }
+
+ data->field_4AA = data->field_4A9;
+ if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
+ data->currentFrame2 = 0;
+ } else {
+ data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
+
+ if (data->currentFrame2 == -1) {
+ clearSequences(entityIndex);
+ return;
+ }
+ }
+
+ data->field_4A9 = field4A9;
+ data->field_49B = data->frame->getInfo()->field_30;
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->direction = kDirectionSwitch;
+ data->directionSwitch = direction;
+ } else {
+ SAFE_DELETE(data->sequence2);
+
+ data->sequence2 = loadSequence1(data->sequence->getName(), data->sequence->getField30());
+
+ data->sequenceName2 = data->sequenceName;
+ data->field_4AA = data->field_4A9;
+ data->field_49B = data->frame->getInfo()->field_30;
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->direction = kDirectionSwitch;
+ data->directionSwitch = direction;
+
+ if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
+ data->currentFrame2 = 0;
+ } else {
+ data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
+
+ if (data->currentFrame2 == -1)
+ clearSequences(entityIndex);
+ }
+ }
+}
+
+void Entities::loadSequence2(EntityIndex entityIndex, Common::String sequenceName, Common::String sequenceName2, byte field30, bool reloadSequence) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->sequenceName2 == sequenceName)
+ return;
+
+ if (data->sequence2)
+ SAFE_DELETE(data->sequence2);
+
+ if (reloadSequence) {
+ TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName, sequenceName2);
+ } else {
+ data->sequenceName2 = sequenceName;
+ }
+}
+
+void Entities::getSequenceName(EntityIndex index, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const {
+ EntityData::EntityCallData *data = getData(index);
+ Position position = getScenes()->get(getState()->scene)->position;
+
+ // reset fields
+ data->field_4A9 = false;
+ data->field_4AA = false;
+
+ switch (direction) {
+ default:
+ break;
+
+ case kDirectionUp:
+ switch (position) {
+ default:
+ break;
+
+ case 1:
+ if (data->entityPosition < kPosition_2587)
+ sequence1 = Common::String::format("%02d%01d-01u.seq", index, data->clothes);
+ break;
+
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ if (data->entityPosition >= kPosition_9270)
+ break;
+
+ if (data->entityPosition >= kPosition_8513) {
+ sequence1 = Common::String::format("%02d%01d-%02deu.seq", index, data->clothes, position);
+ } else {
+ sequence1 = Common::String::format("%02d%01d-03u.seq", index, data->clothes);
+ sequence2 = Common::String::format("%02d%01d-%02deu.seq", index, data->clothes, position);
+ data->field_4A9 = true;
+ }
+ break;
+
+ case 18:
+ if (data->entityPosition < kPosition_9270)
+ sequence1 = Common::String::format("%02d%01d-18u.seq", index, data->clothes);
+ break;
+
+ case 22:
+ if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
+ sequence1 = Common::String::format("%02d%01d-22u.seq", index, data->clothes);
+ break;
+
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ if (getData(kEntityPlayer)->entityPosition <= data->entityPosition)
+ break;
+
+ if (data->entityPosition >= kPosition_2087) {
+ sequence1 = Common::String::format("%02d%01d-38u.seq", index, data->clothes);
+ data->field_4A9 = true;
+ } else {
+ sequence1 = Common::String::format("%02d%01d-%02deu.seq", index, data->clothes, position);
+ sequence2 = Common::String::format("%02d%01d-38u.seq", index, data->clothes);
+ data->field_4AA = true;
+ }
+ break;
+
+ case 40:
+ if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
+ sequence1 = Common::String::format("%02d%01d-40u.seq", index, data->clothes);
+ break;
+ }
+ break;
+
+ case kDirectionDown:
+ switch (position) {
+ default:
+ break;
+
+ case 1:
+ if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
+ sequence1 = Common::String::format("%02d%01d-01d.seq", index, data->clothes);
+ break;
+
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ if (getData(kEntityPlayer)->entityPosition >= data->entityPosition)
+ break;
+
+ if (data->entityPosition <= kPosition_8513) {
+ sequence1 = Common::String::format("%02d%01d-03d.seq", index, data->clothes);
+ data->field_4A9 = true;
+ } else {
+ sequence1 = Common::String::format("%02d%01d-%02ded.seq", index, data->clothes, position);
+ sequence2 = Common::String::format("%02d%01d-03d.seq", index, data->clothes);
+ data->field_4AA = true;
+ }
+ break;
+
+ case 18:
+ if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
+ sequence1 = Common::String::format("%02d%01d-18d.seq", index, data->clothes);
+ break;
+
+ case 22:
+ if (data->entityPosition > kPosition_850)
+ sequence1 = Common::String::format("%02d%01d-22d.seq", index, data->clothes);
+ break;
+
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ if (data->entityPosition <= kPosition_850)
+ break;
+
+ if (data->entityPosition <= kPosition_2087) {
+ sequence1 = Common::String::format("%02d%01d-%02ded.seq", index, data->clothes, position);
+ } else {
+ sequence1 = Common::String::format("%02d%01d-38d.seq", index, data->clothes);
+ sequence2 = Common::String::format("%02d%01d-%02ded.seq", index, data->clothes, position);
+ data->field_4A9 = true;
+ }
+ break;
+
+ case 40:
+ if (getData(kEntityPlayer)->entityPosition > kPosition_8013)
+ sequence1 = Common::String::format("%02d%01d-40d.seq", index, data->clothes);
+ break;
+ }
+ break;
+
+ // First part of sequence is already set
+ case kDirectionLeft:
+ case kDirectionRight:
+ sequence1 = Common::String::format("%s%02d.seq", data->sequenceNamePrefix.c_str(), position);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// Compartments
+//////////////////////////////////////////////////////////////////////////
+void Entities::enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
+ if (entity > kEntityChapters)
+ return;
+
+ switch (compartment) {
+ default:
+ // Return here so we do not update the compartments
+ return;
+
+ case kObjectCompartment1:
+ updatePositionsEnter(entity, kCarGreenSleeping, 41, 51, 17, 38);
+ break;
+
+ case kObjectCompartment2:
+ updatePositionsEnter(entity, kCarGreenSleeping, 42, 52, 15, 36);
+ break;
+
+ case kObjectCompartment3:
+ updatePositionsEnter(entity, kCarGreenSleeping, 43, 53, 13, 34);
+ break;
+
+ case kObjectCompartment4:
+ updatePositionsEnter(entity, kCarGreenSleeping, 44, 54, 11, 32);
+ break;
+
+ case kObjectCompartment5:
+ updatePositionsEnter(entity, kCarGreenSleeping, 45, 55, 9, 30);
+ break;
+
+ case kObjectCompartment6:
+ updatePositionsEnter(entity, kCarGreenSleeping, 46, 56, 7, 28);
+ break;
+
+ case kObjectCompartment7:
+ updatePositionsEnter(entity, kCarGreenSleeping, 47, 57, 5, 26);
+ break;
+
+ case kObjectCompartment8:
+ updatePositionsEnter(entity, kCarGreenSleeping, 48, 58, 3, 25);
+ break;
+
+ case kObjectCompartmentA:
+ updatePositionsEnter(entity, kCarRedSleeping, 41, 51, 17, 38);
+ break;
+
+ case kObjectCompartmentB:
+ updatePositionsEnter(entity, kCarRedSleeping, 42, 52, 15, 36);
+ break;
+
+ case kObjectCompartmentC:
+ updatePositionsEnter(entity, kCarRedSleeping, 43, 53, 13, 34);
+ break;
+
+ case kObjectCompartmentD:
+ updatePositionsEnter(entity, kCarRedSleeping, 44, 54, 11, 32);
+ break;
+
+ case kObjectCompartmentE:
+ updatePositionsEnter(entity, kCarRedSleeping, 45, 55, 9, 30);
+ break;
+
+ case kObjectCompartmentF:
+ updatePositionsEnter(entity, kCarRedSleeping, 46, 56, 7, 28);
+ break;
+
+ case kObjectCompartmentG:
+ updatePositionsEnter(entity, kCarRedSleeping, 47, 57, 5, 26);
+ break;
+
+ case kObjectCompartmentH:
+ updatePositionsEnter(entity, kCarRedSleeping, 48, 58, 3, 25);
+ break;
+ }
+
+ // Update compartments
+ int index = (compartment < 32 ? compartment - 1 : compartment - 24);
+ if (index >= 16)
+ error("Entities::exitCompartment: invalid compartment index!");
+
+ if (useCompartment1)
+ _compartments1[index] |= STORE_VALUE(entity);
+ else
+ _compartments[index] |= STORE_VALUE(entity);
+}
+
+void Entities::exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
+ if (entity > kEntityChapters)
+ return;
+
+ // TODO factorize in one line
+ switch (compartment) {
+ default:
+ // Return here so we do not update the compartments
+ return;
+
+ case kObjectCompartment1:
+ updatePositionsExit(entity, kCarGreenSleeping, 41, 51);
+ break;
+
+ case kObjectCompartment2:
+ updatePositionsExit(entity, kCarGreenSleeping, 42, 52);
+ break;
+
+ case kObjectCompartment3:
+ updatePositionsExit(entity, kCarGreenSleeping, 43, 53);
+ break;
+
+ case kObjectCompartment4:
+ updatePositionsExit(entity, kCarGreenSleeping, 44, 54);
+ break;
+
+ case kObjectCompartment5:
+ updatePositionsExit(entity, kCarGreenSleeping, 45, 55);
+ break;
+
+ case kObjectCompartment6:
+ updatePositionsExit(entity, kCarGreenSleeping, 46, 56);
+ break;
+
+ case kObjectCompartment7:
+ updatePositionsExit(entity, kCarGreenSleeping, 47, 57);
+ break;
+
+ case kObjectCompartment8:
+ updatePositionsExit(entity, kCarGreenSleeping, 48, 58);
+ break;
+
+ case kObjectCompartmentA:
+ updatePositionsExit(entity, kCarRedSleeping, 41, 51);
+ break;
+
+ case kObjectCompartmentB:
+ updatePositionsExit(entity, kCarRedSleeping, 42, 52);
+ break;
+
+ case kObjectCompartmentC:
+ updatePositionsExit(entity, kCarRedSleeping, 43, 53);
+ break;
+
+ case kObjectCompartmentD:
+ updatePositionsExit(entity, kCarRedSleeping, 44, 54);
+ break;
+
+ case kObjectCompartmentE:
+ updatePositionsExit(entity, kCarRedSleeping, 45, 55);
+ break;
+
+ case kObjectCompartmentF:
+ updatePositionsExit(entity, kCarRedSleeping, 46, 56);
+ break;
+
+ case kObjectCompartmentG:
+ updatePositionsExit(entity, kCarRedSleeping, 47, 57);
+ break;
+
+ case kObjectCompartmentH:
+ updatePositionsExit(entity, kCarRedSleeping, 48, 58);
+ break;
+ }
+
+ // Update compartments
+ int index = (compartment < 32 ? compartment - 1 : compartment - 24);
+ if (index >= 16)
+ error("Entities::exitCompartment: invalid compartment index!");
+
+ if (useCompartment1)
+ _compartments1[index] &= ~STORE_VALUE(entity);
+ else
+ _compartments[index] &= ~STORE_VALUE(entity);
+}
+
+void Entities::updatePositionEnter(EntityIndex entity, CarIndex car, Position position) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position] |= STORE_VALUE(entity);
+
+ if (isPlayerPosition(car, position) || (car == kCarRestaurant && position == 57 && isPlayerPosition(kCarRestaurant, 50))) {
+ getSound()->excuseMe(entity);
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ } else {
+ getLogic()->updateCursor();
+ }
+}
+
+void Entities::updatePositionExit(EntityIndex entity, CarIndex car, Position position) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position] &= ~STORE_VALUE(entity);
+
+ getLogic()->updateCursor();
+}
+
+void Entities::updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position1] |= STORE_VALUE(entity);
+ _positions[100 * car + position2] |= STORE_VALUE(entity);
+
+ // FIXME: also checking two DWORD values that do not seem to updated anywhere...
+ if (isPlayerPosition(car, position1) || isPlayerPosition(car, position2) || isPlayerPosition(car, position3) || isPlayerPosition(car, position4)) {
+ getSound()->excuseMe(entity);
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ } else {
+ getLogic()->updateCursor();
+ }
+}
+
+void Entities::updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position1] &= ~STORE_VALUE(entity);
+ _positions[100 * car + position2] &= ~STORE_VALUE(entity);
+
+ getLogic()->updateCursor();
+}
+
+void Entities::loadSceneFromEntityPosition(CarIndex car, EntityPosition entityPosition, bool alternate) const {
+
+ // Determine position
+ Position position = (alternate ? 1 : 40);
+ do {
+ if (entityPosition > entityPositions[position]) {
+ if (alternate)
+ break;
+
+ // For default value, we ignore position 24
+ if (position != 24)
+ break;
+ }
+
+ alternate ? ++position : --position;
+
+ } while (alternate ? position <= 18 : position >= 22);
+
+ // For position outside bounds, use minimal value
+ if ((alternate && position > 18) || (alternate && position < 22)) {
+ getScenes()->loadSceneFromPosition(car, alternate ? 18 : 22);
+ return;
+ }
+
+ // Load scene from position
+ switch (position) {
+ default:
+ getScenes()->loadSceneFromPosition(car, (Position)(position + (alternate ? - 1 : 1)));
+ break;
+
+ // Alternate
+ case 1:
+ if (alternate) getScenes()->loadSceneFromPosition(car, 1);
+ break;
+
+ case 5:
+ if (alternate) getScenes()->loadSceneFromPosition(car, 3);
+ break;
+
+ // Default
+ case 23:
+ if (!alternate) getScenes()->loadSceneFromPosition(car, 25);
+ break;
+
+ case 40:
+ if (!alternate) getScenes()->loadSceneFromPosition(car, 40);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+bool Entities::hasValidFrame(EntityIndex entity) const {
+ return (getData(entity)->frame && (getData(entity)->frame->getInfo()->subType != kFrameType3));
+}
+
+bool Entities::compare(EntityIndex entity1, EntityIndex entity2) const {
+ EntityData::EntityCallData *data1 = getData(entity1);
+ EntityData::EntityCallData *data2 = getData(entity2);
+
+ if (data2->car != data1->car
+ || data1->car < kCarGreenSleeping
+ || data1->car > kCarRedSleeping)
+ return false;
+
+ EntityPosition position1 = (data1->entityPosition >= data2->entityPosition) ? data1->entityPosition : data2->entityPosition;
+ EntityPosition position2 = (data1->entityPosition >= data2->entityPosition) ? data2->entityPosition : data1->entityPosition;
+
+ // Compute position
+ int index1 = 7;
+ do {
+ if (objectsPosition[index1] >= position2)
+ break;
+
+ --index1;
+ } while (index1 > -1);
+
+ int index2 = 0;
+ do {
+ if (objectsPosition[index2] <= position2)
+ break;
+
+ ++index2;
+ } while (index2 < 8);
+
+ if (index1 > -1 && index2 < 8 && index2 <= index1) {
+ while (index2 <= index1) {
+ if (getCompartments(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
+ return true;
+
+ if (getCompartments1(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
+ return true;
+
+ ++index2;
+ }
+ }
+
+ for (EntityIndex entity = kEntityAnna; entity <= kEntity39; entity = (EntityIndex)(entity + 1)) {
+
+ if (entity1 == entity || entity2 == entity)
+ continue;
+
+ if (!isDirectionUpOrDown(entity))
+ continue;
+
+ if (data1->car == getEntityData(entity)->car
+ && getEntityData(entity)->entityPosition > position2
+ && getEntityData(entity)->entityPosition < position1)
+ return true;
+ }
+
+ return false;
+}
+
+bool Entities::updateEntity(EntityIndex entity, CarIndex car, EntityPosition position) const {
+ EntityData::EntityCallData *data = getData(entity);
+ EntityDirection direction = kDirectionNone;
+ int delta = 0;
+ bool flag1 = false;
+ bool flag2 = false;
+ bool flag3 = false;
+
+ if (position == kPosition_2000
+ && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
+ && !isPlayerPosition(kCarGreenSleeping, 1)
+ && !isPlayerPosition(kCarRedSleeping, 2))
+ position = kPosition_1500;
+
+ if (data->direction != kDirectionUp && data->direction != kDirectionDown)
+ data->field_497 = 0;
+
+ if (data->field_497) {
+ data->field_497--;
+
+ if (data->field_497 == 128)
+ data->field_497 = 0;
+
+ if ((data->field_497 & 127) != 8) {
+ data->field_49B = 0;
+ return false;
+ }
+
+ flag1 = true;
+
+ if (data->field_497 & 128)
+ flag2 = true;
+ }
+
+ if (data->car != car)
+ goto label_process_entity;
+
+ // Calculate delta
+ delta = ABS(data->entityPosition - position);
+ if (delta < 100 || (position > kPosition_850 && position < kPosition_9270 && delta < 300))
+ flag3 = true;
+
+ if (!flag3) {
+ if ((getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && data->direction == kDirectionUp)
+ || (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && data->direction == kDirectionDown)) {
+ if (!checkPosition(position) && isDistanceBetweenEntities(entity, kEntityPlayer, 250))
+ flag3 = true;
+ }
+
+ if (!flag3)
+ goto label_process_entity;
+ }
+
+ if (getEntities()->hasValidFrame(entity)
+ && getEntities()->isWalkingOppositeToPlayer(entity)
+ && !getEntities()->checkPosition(position)) {
+ flag3 = false;
+ position = (EntityPosition)(getData(kEntityPlayer)->entityPosition + 250 * (data->direction == kDirectionUp ? 1 : -1));
+ }
+
+ if (!flag3) {
+label_process_entity:
+
+ // Calculate direction
+ if (data->car < car)
+ direction = kDirectionUp;
+ else if (data->car > car)
+ direction = kDirectionDown;
+ else // same car
+ direction = (data->entityPosition < position) ? kDirectionUp : kDirectionDown;
+
+ if (data->direction == direction) {
+ if (!flag1) {
+
+ if (checkDistanceFromPosition(entity, kPosition_1500, 750) && entity != kEntityFrancois) {
+
+ if (data->entity != kEntityPlayer) {
+ if (data->direction != kDirectionUp || (position <= kPosition_2000 && data->car == car)) {
+ if (data->direction == kDirectionDown && (position < kPosition_1500 || data->car != car)) {
+ if (data->entityPosition > kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
+ data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
+ getSavePoints()->push(entity, data->entity, kAction11);
+ }
+ }
+ } else {
+ if (data->entityPosition < kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
+ data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
+ getSavePoints()->push(entity, data->entity, kAction11, 1);
+ }
+ }
+ }
+
+ } else if (data->entity) {
+ getSavePoints()->push(entity, data->entity, kAction16);
+ data->entity = kEntityPlayer;
+ }
+
+ if (hasValidFrame(entity)) {
+
+ if (!data->field_4A9)
+ return false;
+
+ int compartmentIndex = 0;
+ if (data->car == kCarGreenSleeping)
+ compartmentIndex = 0;
+ else if (data->car == kCarRedSleeping)
+ compartmentIndex = 8;
+
+ for (int i = 0; i < 8; i++) {
+ if (getCompartments(compartmentIndex) || getCompartments1(compartmentIndex)) {
+ if (checkDistanceFromPosition(entity, objectsPosition[i], 750)) {
+ if (checkPosition(objectsPosition[i])) {
+
+ if ((data->direction == kDirectionUp && data->entityPosition < objectsPosition[i] && (data->car != car || position > objectsPosition[i]))
+ || (data->direction == kDirectionDown && data->entityPosition > objectsPosition[i] && (data->car != car || position < objectsPosition[i]))) {
+
+ getSound()->excuseMe(entity, (EntityIndex)(State::getPowerOfTwo((uint32)(getCompartments(compartmentIndex) ? getCompartments(compartmentIndex) : getCompartments1(compartmentIndex)))));
+
+ data->field_497 = 144;
+
+ break;
+ }
+ }
+ }
+ }
+
+ compartmentIndex++;
+ }
+
+ for (EntityIndex entityIndex = kEntityAnna; entityIndex <= kEntity39; entityIndex = (EntityIndex)(entityIndex + 1)) {
+ if (getSavePoints()->getCallback(entityIndex)
+ && hasValidFrame(entityIndex)
+ && entityIndex != entity
+ && isDistanceBetweenEntities(entity, entityIndex, 750)
+ && isDirectionUpOrDown(entityIndex)
+ && (entity != kEntityRebecca || entityIndex != kEntitySophie)
+ && (entity != kEntitySophie || entityIndex != kEntityRebecca)
+ && (entity != kEntityIvo || entityIndex != kEntitySalko)
+ && (entity != kEntitySalko || entityIndex != kEntityIvo)
+ && (entity != kEntityMilos || entityIndex != kEntityVesna)
+ && (entity != kEntityVesna || entityIndex != kEntityMilos)) {
+
+ EntityData::EntityCallData *data2 = getData(entityIndex);
+
+ if (data->direction != data2->direction) {
+
+ if ((data->direction != kDirectionUp || data2->entityPosition <= data->entityPosition)
+ && (data->direction != kDirectionDown || data2->entityPosition >= data->entityPosition))
+ continue;
+
+ data->field_49B = 0;
+ data2->field_49B = 0;
+
+ data->field_497 = 16;
+ data2->field_497 = 16;
+
+ getSound()->excuseMe(entity, entityIndex);
+ getSound()->excuseMe(entityIndex, entity);
+
+ if (entityIndex > entity)
+ ++data2->field_497;
+
+ break;
+ }
+
+ if (ABS(data2->entityPosition - getData(kEntityPlayer)->entityPosition) < ABS(data->entityPosition - getData(kEntityPlayer)->entityPosition)) {
+
+ if (!isWalkingOppositeToPlayer(entity)) {
+
+ if (direction == kDirectionUp) {
+ if (data->entityPosition < kPosition_9500)
+ data->entityPosition = (EntityPosition)(data->entityPosition + 500);
+ } else {
+ if (data->entityPosition > kPosition_500)
+ data->entityPosition = (EntityPosition)(data->entityPosition - 500);
+ }
+
+ drawSequences(entity, direction, true);
+
+ return false;
+ }
+ data->field_49B = 0;
+
+ break;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ if (data->direction == kDirectionUp) {
+ if (data->entityPosition + data->field_4A3 < 10000)
+ data->entityPosition = (EntityPosition)(data->entityPosition + data->field_4A3);
+ } else {
+ if (data->entityPosition > data->field_4A3)
+ data->entityPosition = (EntityPosition)(data->entityPosition - data->field_4A3);
+ }
+
+ if (data->entityPosition <= kPosition_9270 || data->direction != kDirectionUp) {
+ if (data->entityPosition < kPosition_850 && data->direction == kDirectionDown) {
+ if (changeCar(data, entity, car, position, false, kPosition_9269, kCarKronos))
+ return true;
+ }
+ } else {
+ if (changeCar(data, entity, car, position, true, kPosition_851, kCarGreenSleeping))
+ return true;
+ }
+
+ if (getData(kEntityPlayer)->car == data->car && data->location == kLocationOutsideCompartment) {
+ if (data->direction == kDirectionUp) {
+
+ if (getData(kEntityPlayer)->entityPosition > data->entityPosition
+ && getData(kEntityPlayer)->entityPosition - data->entityPosition >= 500
+ && data->field_4A3 + 500 > getData(kEntityPlayer)->entityPosition - data->entityPosition) {
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkCurrentPosition(false)) {
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
+
+ if (getScenes()->checkCurrentPosition(false))
+ getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1, true);
+
+ } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)) {
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
+ }
+ }
+ } else {
+ if (getData(kEntityPlayer)->entityPosition < data->entityPosition
+ && data->entityPosition - getData(kEntityPlayer)->entityPosition >= 500
+ && data->field_4A3 + 500 > data->entityPosition - getData(kEntityPlayer)->entityPosition) {
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) {
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
+ } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)){
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
+
+ if (getScenes()->checkCurrentPosition(false))
+ getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1);
+ }
+ }
+ }
+ return false;
+ }
+ }
+ } else if (!flag1) {
+ drawSequences(entity, direction, true);
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust positions
+
+ // Direction Up
+ if (direction == kDirectionUp) {
+ if (data->entityPosition < (flag2 ? kPosition_8800 : kPosition_9250))
+ data->entityPosition = (EntityPosition)(data->entityPosition + (flag2 ? kPosition_1200 : kPosition_750));
+
+ if (data->car == car && data->entityPosition >= position) {
+ data->entityPosition = position;
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+ return true;
+ }
+
+ drawSequences(entity, direction, true);
+ return false;
+ }
+
+ // Direction Down
+ if (data->entityPosition > (flag2 ? kPosition_1200 : kPosition_750))
+ data->entityPosition = (EntityPosition)(data->entityPosition - (flag2 ? kPosition_1200 : kPosition_750));
+
+ if (data->car == car && data->entityPosition <= position) {
+ data->entityPosition = position;
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+ return true;
+ }
+
+ drawSequences(entity, direction, true);
+ return false;
+ }
+
+ data->entityPosition = position;
+ if (data->direction == kDirectionUp || data->direction == kDirectionDown)
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+
+ return true;
+}
+
+bool Entities::changeCar(EntityData::EntityCallData *data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const {
+ if (getData(kEntityPlayer)->car == data->car) {
+ getSound()->playSoundEvent(entity, 36);
+ getSound()->playSoundEvent(entity, 37, 30);
+ }
+
+ data->car = (CarIndex)(increment ? data->car + 1 : data->car - 1);
+ data->entityPosition = newPosition;
+
+ if (data->car == newCar) {
+ if (isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->excuseMe(entity, kEntityPlayer, SoundManager::kFlagDefault);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 1);
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ getSound()->playSoundEvent(kEntityPlayer, 15);
+ }
+ }
+
+ if ((increment ? data->car > car : data->car < car) || (data->car == car && (increment ? data->entityPosition >= position : data->entityPosition <= position))) {
+ data->car = car;
+ data->entityPosition = position;
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+
+ return true;
+ }
+
+ if (data->car == newCar) {
+ if (isInKronosCarEntrance(kEntityPlayer)) {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->excuseMe(entity, kEntityPlayer, SoundManager::kFlagDefault);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 62);
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ getSound()->playSoundEvent(kEntityPlayer, 15);
+ }
+ }
+
+ if (data->car == getData(kEntityPlayer)->car) {
+ getSound()->playSoundEvent(entity, 36);
+ getSound()->playSoundEvent(entity, 37, 30);
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// CHECKS
+//////////////////////////////////////////////////////////////////////////
+bool Entities::isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const {
+ return (getData(entity)->entityPosition == position
+ && getData(entity)->location == kLocationInsideCompartment
+ && getData(entity)->car == car);
+}
+
+bool Entities::checkFields2(ObjectIndex object) const {
+
+ EntityPosition position = kPositionNone;
+ CarIndex car = kCarNone;
+
+ switch (object) {
+ default:
+ return false;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ position = objectsPosition[object - 1];
+ car = kCarGreenSleeping;
+ if (isInsideCompartment(kEntityPlayer, car, position))
+ return false;
+ break;
+
+ case kObjectHandleBathroom:
+ case kObjectHandleInsideBathroom:
+ case kObjectKitchen:
+ case kObject20:
+ case kObject21:
+ case kObject22:
+ position = objectsPosition[object-17];
+ car = kCarGreenSleeping;
+ break;
+
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ case kObjectCompartmentG:
+ case kObjectCompartmentH:
+ position = objectsPosition[object-32];
+ car = kCarRedSleeping;
+ if (isInsideCompartment(kEntityPlayer, car, position))
+ return false;
+ break;
+
+ case kObject48:
+ case kObject49:
+ case kObject50:
+ case kObject51:
+ case kObject52:
+ case kObject53:
+ position = objectsPosition[object-48];
+ car = kCarRedSleeping;
+ break;
+
+ }
+
+ uint index = 1;
+ while (!isInsideCompartment((EntityIndex)index, car, position) || index == kEntityVassili) {
+ index++;
+ if (index >= 40)
+ return false;
+ }
+
+ return true;
+}
+
+bool Entities::isInsideCompartments(EntityIndex entity) const {
+ return (getData(entity)->car == kCarGreenSleeping
+ || getData(entity)->car == kCarRedSleeping)
+ && getData(entity)->location == kLocationInsideCompartment;
+}
+
+bool Entities::isPlayerPosition(CarIndex car, Position position) const {
+ return getData(kEntityPlayer)->car == car && getScenes()->get(getState()->scene)->position == position;
+}
+
+bool Entities::isInsideTrainCar(EntityIndex entity, CarIndex car) const {
+ return getData(entity)->car == car && getData(entity)->location <= kLocationInsideCompartment;
+}
+
+bool Entities::isInGreenCarEntrance(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarGreenSleeping) && getData(entity)->entityPosition < kPosition_850;
+}
+
+bool Entities::isPlayerInCar(CarIndex car) const {
+ return isInsideTrainCar(kEntityPlayer, car) && getData(kEntityPlayer)->location && !isInGreenCarEntrance(kEntityPlayer);
+}
+
+bool Entities::isDirectionUpOrDown(EntityIndex entity) const {
+ return getData(entity)->direction == kDirectionUp || getData(entity)->direction == kDirectionDown;
+}
+
+bool Entities::isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const {
+ return getData(entity1)->car == getData(entity2)->car
+ && (uint)ABS(getData(entity1)->entityPosition - getData(entity2)->entityPosition) <= distance
+ && (getData(entity1)->location != kLocationOutsideTrain || getData(entity2)->location != kLocationOutsideTrain);
+}
+
+bool Entities::checkFields10(EntityIndex entity) const {
+ return getData(entity)->location <= kLocationOutsideTrain;
+}
+
+bool Entities::isSomebodyInsideRestaurantOrSalon() const {
+ for (uint i = 1; i < _entities.size(); i++) {
+ EntityIndex index = (EntityIndex)i;
+
+ if (getData(index)->location == kLocationOutsideCompartment && (isInSalon(index) || isInRestaurant(index)))
+ return false;
+ }
+
+ return true;
+}
+
+bool Entities::isInSalon(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarRestaurant)
+ && getData(entity)->entityPosition >= kPosition_1540
+ && getData(entity)->entityPosition <= kPosition_3650;
+}
+
+bool Entities::isInRestaurant(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarRestaurant)
+ && getData(entity)->entityPosition >= kPosition_3650
+ && getData(entity)->entityPosition <= kPosition_5800;
+}
+
+bool Entities::isInKronosSalon(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarKronos)
+ && getData(entity)->entityPosition >= kPosition_5500
+ && getData(entity)->entityPosition <= kPosition_7500;
+}
+
+bool Entities::isOutsideAlexeiWindow() const {
+ return (getData(kEntityPlayer)->entityPosition == kPosition_7500 || getData(kEntityPlayer)->entityPosition == kPosition_8200)
+ && getData(kEntityPlayer)->location == kLocationOutsideTrain
+ && getData(kEntityPlayer)->car == kCarGreenSleeping;
+}
+
+bool Entities::isOutsideAnnaWindow() const {
+ return (getData(kEntityPlayer)->entityPosition == kPosition_4070 || getData(kEntityPlayer)->entityPosition == kPosition_4840)
+ && getData(kEntityPlayer)->location == kLocationOutsideTrain
+ && getData(kEntityPlayer)->car == kCarRedSleeping;
+}
+
+bool Entities::isInKitchen(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarRestaurant) && getData(entity)->entityPosition > kPosition_5800;
+}
+
+bool Entities::isNobodyInCompartment(CarIndex car, EntityPosition position) const {
+ for (uint i = 1; i < _entities.size(); i++) {
+ if (isInsideCompartment((EntityIndex)i, car, position))
+ return false;
+ }
+ return true;
+}
+
+bool Entities::checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const {
+
+ if (getData(entity)->car != car || getData(entity)->location != kLocationInsideCompartment)
+ return false;
+
+ EntityPosition entityPosition = getData(entity)->entityPosition;
+
+ // Test values
+ if (position == kPosition_4455) {
+ if (entityPosition == kPosition_4070 || entityPosition == kPosition_4455 || entityPosition == kPosition_4840)
+ return true;
+
+ return false;
+ }
+
+ if (position == kPosition_6130) {
+ if (entityPosition == kPosition_5790 || entityPosition == kPosition_6130 || entityPosition == kPosition_6470)
+ return true;
+
+ return false;
+ }
+
+ if (position != kPosition_7850
+ || (entityPosition != kPosition_7500 && entityPosition != kPosition_7850 && entityPosition != kPosition_8200))
+ return false;
+
+ return true;
+}
+
+bool Entities::isInBaggageCarEntrance(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarBaggage)
+ && getData(entity)->entityPosition >= kPosition_4500
+ && getData(entity)->entityPosition <= kPosition_5500;
+}
+
+bool Entities::isInBaggageCar(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarBaggage) && getData(entity)->entityPosition < kPosition_4500;
+}
+
+bool Entities::isInKronosSanctum(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarKronos)
+ && getData(entity)->entityPosition >= kPosition_3500
+ && getData(entity)->entityPosition <= kPosition_5500;
+}
+
+bool Entities::isInKronosCarEntrance(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarKronos) && getData(entity)->entityPosition > kPosition_7900;
+}
+
+bool Entities::checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const {
+ return distance >= ABS(getData(entity)->entityPosition - position);
+}
+
+bool Entities::isWalkingOppositeToPlayer(EntityIndex entity) const {
+ if (getData(entity)->direction == kDirectionUp && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
+ return true;
+
+ return (getData(entity)->direction == kDirectionDown && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp));
+}
+
+bool Entities::isFemale(EntityIndex entity) {
+ return (entity == kEntityAnna
+ || entity == kEntityTatiana
+ || entity == kEntityVesna
+ || entity == kEntityKahina
+ || entity == kEntityMmeBoutarel
+ || entity == kEntityRebecca
+ || entity == kEntitySophie
+ || entity == kEntityYasmin
+ || entity == kEntityHadija
+ || entity == kEntityAlouan);
+}
+
+bool Entities::isMarried(EntityIndex entity) {
+ return (entity != kEntityTatiana
+ && entity != kEntityRebecca
+ && entity != kEntitySophie);
+}
+
+bool Entities::checkPosition(EntityPosition position) const {
+ Position position1 = 0;
+ Position position2 = 0;
+
+ switch (position) {
+ default:
+ return true;
+
+ case kPosition_1500:
+ position1 = 1;
+ position2 = 23;
+ break;
+
+ case kPosition_2740:
+ position1 = 3;
+ position2 = 25;
+ break;
+
+ case kPosition_3050:
+ position1 = 5;
+ position2 = 26;
+ break;
+
+ case kPosition_4070:
+ position1 = 7;
+ position2 = 28;
+ break;
+
+ case kPosition_4840:
+ position1 = 9;
+ position2 = 30;
+ break;
+
+ case kPosition_5790:
+ position1 = 11;
+ position2 = 32;
+ break;
+
+ case kPosition_6470:
+ position1 = 13;
+ position2 = 34;
+ break;
+
+ case kPosition_7500:
+ position1 = 15;
+ position2 = 36;
+ break;
+
+ case kPosition_8200:
+ position1 = 17;
+ position2 = 38;
+ break;
+ }
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && entityPositions[position1] >= getEntityData(kEntityPlayer)->entityPosition)
+ return true;
+ else
+ return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && entityPositions[position2] <= getEntityData(kEntityPlayer)->entityPosition);
+}
+
+bool Entities::checkSequenceFromPosition(EntityIndex entity) const {
+ FrameInfo *info = getEntityData(entity)->sequence->getFrameInfo((uint16)getEntityData(entity)->currentFrame);
+
+ if (getEntityData(entity)->direction == kDirectionUp)
+ return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
+ && info->entityPosition + getEntityPositionFromCurrentPosition() > kPosition_8513);
+
+ if (getEntityData(entity)->direction == kDirectionDown)
+ return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)
+ && info->entityPosition + getEntityPositionFromCurrentPosition() < kPosition_2087);
+
+ return false;
+}
+
+EntityPosition Entities::getEntityPositionFromCurrentPosition() const {
+ // Get the scene position first
+ Position position = getScenes()->get(getState()->scene)->position;
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
+ return (EntityPosition)(entityPositions[position] - kPosition_1430);
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
+ return (EntityPosition)(entityPositions[position] - kPosition_9020);
+
+ return kPositionNone;
+}
+
+void Entities::clearEntitySequenceData(EntityData::EntityCallData *data, EntityDirection direction) const {
+ getScenes()->removeAndRedraw(&data->frame, false);
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ SAFE_DELETE(data->sequence);
+ SAFE_DELETE(data->sequence2);
+
+ data->sequenceName = "";
+ data->sequenceName2 = "";
+
+ data->field_4A9 = false;
+ data->field_4AA = false;
+ data->directionSwitch = kDirectionNone;
+
+ data->currentFrame = -1;
+ data->currentFrame2 = 0;
+
+ data->direction = direction;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h
new file mode 100644
index 0000000000..8cc2072c42
--- /dev/null
+++ b/engines/lastexpress/game/entities.h
@@ -0,0 +1,380 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITIES_H
+#define LASTEXPRESS_ENTITIES_H
+
+/*
+ Entities
+ --------
+
+ The entities structure contains 40 Entity_t structures for each entity
+
+*/
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/rect.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+
+class Entities : Common::Serializable {
+public:
+ Entities(LastExpressEngine *engine);
+ ~Entities();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+ void savePositions(Common::Serializer &ser);
+ void saveCompartments(Common::Serializer &ser);
+
+ void setup(bool isFirstChapter, EntityIndex entity);
+ void setupChapter(ChapterIndex chapter);
+ void reset();
+
+ // Update & drawing
+
+ /**
+ * Reset an entity state
+ *
+ * @param entity entity index
+ * @note remember to call the function pointer (we do not pass it our implementation)
+ */
+ void resetState(EntityIndex entity);
+ void updateFields() const;
+ void updateSequences() const;
+ void updateCallbacks();
+
+ EntityIndex canInteractWith(const Common::Point &point) const;
+ bool compare(EntityIndex entity1, EntityIndex entity2) const;
+
+ /**
+ * Update an entity current sequence frame (and related fields)
+ *
+ * @param entity entity index
+ */
+ void updateFrame(EntityIndex entity) const;
+ void updatePositionEnter(EntityIndex entity, CarIndex car, Position position);
+ void updatePositionExit(EntityIndex entity, CarIndex car, Position position);
+ void enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1 = false);
+ void exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1 = false);
+
+ // Sequences
+ void drawSequenceLeft(EntityIndex index, const char *sequence) const;
+ void drawSequenceRight(EntityIndex index, const char *sequence) const;
+ void clearSequences(EntityIndex index) const;
+
+ bool updateEntity(EntityIndex entity, CarIndex car, EntityPosition position) const;
+ bool hasValidFrame(EntityIndex entity) const;
+
+ // Accessors
+ Entity *get(EntityIndex entity);
+ EntityData::EntityCallData *getData(EntityIndex entity) const;
+ int getPosition(CarIndex car, Position position) const;
+ int getCompartments(int index) const;
+ int getCompartments1(int index) const;
+
+ // Scene
+ void loadSceneFromEntityPosition(CarIndex car, EntityPosition position, bool alternate = false) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Checks
+ //////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Query if 'entity' is inside a compartment
+ *
+ * @param entity The entity.
+ * @param car The car.
+ * @param position The position.
+ *
+ * @return true if inside the compartment, false if not.
+ */
+ bool isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const;
+
+ bool checkFields2(ObjectIndex object) const;
+
+ /**
+ * Query if 'entity' is in compartment cars.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in compartment cars, false if not.
+ */
+ bool isInsideCompartments(EntityIndex entity) const;
+
+ /**
+ * Query if the player is in the specified position
+ *
+ * @param car The car.
+ * @param position The position.
+ * @return true if player is in that position, false if not.
+ */
+ bool isPlayerPosition(CarIndex car, Position position) const;
+
+ /**
+ * Query if 'entity' is inside a train car
+ *
+ * @param entity The entity.
+ * @param car The car.
+ *
+ * @return true if inside a train car, false if not.
+ */
+ bool isInsideTrainCar(EntityIndex entity, CarIndex car) const;
+
+ /**
+ * Query if 'entity' is in green car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the green car entrance, false if not.
+ */
+ bool isInGreenCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Query if the player is in a specific car
+ *
+ * @param car The car.
+ *
+ * @return true if player is in the car, false if not.
+ */
+ bool isPlayerInCar(CarIndex car) const;
+
+ /**
+ * Query if 'entity' is going in the up or down direction.
+ *
+ * @param entity The entity.
+ *
+ * @return true if direction is up or down, false if not.
+ */
+ bool isDirectionUpOrDown(EntityIndex entity) const;
+
+ /**
+ * Query if the distance between the two entities is less 'distance'
+ *
+ * @param entity1 The first entity.
+ * @param entity2 The second entity.
+ * @param distance The distance.
+ *
+ * @return true if the distance between entities is less than 'distance', false if not.
+ */
+ bool isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const;
+
+ bool checkFields10(EntityIndex entity) const;
+
+ /**
+ * Query if there is somebody in the restaurant or salon.
+ *
+ * @return true if somebody is in the restaurant or salon, false if not.
+ */
+ bool isSomebodyInsideRestaurantOrSalon() const;
+
+ /**
+ * Query if 'entity' is in the salon.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the salon, false if not.
+ */
+ bool isInSalon(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in the restaurant.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the restaurant, false if not.
+ */
+ bool isInRestaurant(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos salon.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos salon, false if not.
+ */
+ bool isInKronosSalon(EntityIndex entity) const;
+
+ /**
+ * Query if the player is outside Alexei window.
+ *
+ * @return true if outside alexei window, false if not.
+ */
+ bool isOutsideAlexeiWindow() const;
+
+ /**
+ * Query if the player is outside Anna window.
+ *
+ * @return true if outside anna window, false if not.
+ */
+ bool isOutsideAnnaWindow() const;
+
+ /**
+ * Query if 'entity' is in the kitchen.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the kitchen, false if not.
+ */
+ bool isInKitchen(EntityIndex entity) const;
+
+ /**
+ * Query if nobody is in a compartment at that position.
+ *
+ * @param car The car.
+ * @param position The position.
+ *
+ * @return true if nobody is in a compartment, false if not.
+ */
+ bool isNobodyInCompartment(CarIndex car, EntityPosition position) const;
+
+ bool checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const;
+
+ /**
+ * Query if 'entity' is in the baggage car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the baggage car entrance, false if not.
+ */
+ bool isInBaggageCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in the baggage car.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the baggage car, false if not.
+ */
+ bool isInBaggageCar(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos sanctum.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos sanctum, false if not.
+ */
+ bool isInKronosSanctum(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos car entrance, false if not.
+ */
+ bool isInKronosCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Check distance from position.
+ *
+ * @param entity The entity.
+ * @param position The position.
+ * @param distance The distance.
+ *
+ * @return true if distance is bigger, false otherwise.
+ */
+ bool checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const;
+
+ /**
+ * Query if 'entity' is walking opposite to player.
+ *
+ * @param entity The entity.
+ *
+ * @return true if walking opposite to player, false if not.
+ */
+ bool isWalkingOppositeToPlayer(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is female.
+ *
+ * @param entity The entity.
+ *
+ * @return true if female, false if not.
+ */
+ static bool isFemale(EntityIndex entity);
+
+ /**
+ * Query if 'entity' is married.
+ *
+ * @param entity The entity.
+ *
+ * @return true if married, false if not.
+ */
+ static bool isMarried(EntityIndex entity);
+
+private:
+ static const int _compartmentsCount = 16;
+ static const int _positionsCount = 100 * 10; // 100 positions per train car
+
+ LastExpressEngine *_engine;
+ EntityData *_header;
+ Common::Array<Entity *> _entities;
+
+ // Compartments & positions
+ uint _compartments[_compartmentsCount];
+ uint _compartments1[_compartmentsCount];
+ uint _positions[_positionsCount];
+
+ void executeCallbacks();
+ void processEntity(EntityIndex entity);
+
+ void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const;
+ void drawSequences(EntityIndex entity, EntityDirection direction, bool loadSequence) const;
+ void loadSequence2(EntityIndex entity, Common::String sequenceName, Common::String sequenceName2, byte field30, bool loadSequence) const;
+
+ void clearEntitySequenceData(EntityData::EntityCallData *data, EntityDirection direction) const;
+ void computeCurrentFrame(EntityIndex entity) const;
+ int16 getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const;
+ void processFrame(EntityIndex entity, bool keepPreviousFrame, bool dontPlaySound);
+ void drawNextSequence(EntityIndex entity) const;
+ void updateEntityPosition(EntityIndex entity) const;
+ void copySequenceData(EntityIndex entity) const;
+
+ bool changeCar(EntityData::EntityCallData *data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const;
+
+ void getSequenceName(EntityIndex entity, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const;
+
+ void updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4);
+ void updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2);
+
+ void resetSequences(EntityIndex entity) const;
+
+ bool checkPosition(EntityPosition position) const;
+ bool checkSequenceFromPosition(EntityIndex entity) const;
+ EntityPosition getEntityPositionFromCurrentPosition() const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITIES_H
diff --git a/engines/lastexpress/game/fight.cpp b/engines/lastexpress/game/fight.cpp
new file mode 100644
index 0000000000..9f92320575
--- /dev/null
+++ b/engines/lastexpress/game/fight.cpp
@@ -0,0 +1,1586 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/fight.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/func.h"
+
+namespace LastExpress {
+
+#define CALL_FUNCTION0(fighter, name) \
+ (*fighter->name)(fighter)
+
+#define CALL_FUNCTION1(fighter, name, a) \
+ (*fighter->name)(fighter, a)
+
+#define REGISTER_PLAYER_FUNCTIONS(name) \
+ if (!_data) \
+ error("Fight::load##namePlayer - invalid data!"); \
+ _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction##name); \
+ _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update##name); \
+ _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract##name);
+
+#define REGISTER_OPPONENT_FUNCTIONS(name) \
+ if (!_data) \
+ error("Fight::load##nameOpponent - invalid data!"); \
+ _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleOpponentAction##name); \
+ _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponent##name); \
+ _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+#define CHECK_SEQUENCE2(fighter, value) \
+ (fighter->frame->getInfo()->field_33 & value)
+
+Fight::Fight(LastExpressEngine *engine) : _engine(engine), _data(NULL), _endType(kFightEndLost), _state(0), _handleTimer(false) {}
+
+Fight::~Fight() {
+ clearData();
+ _data = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Events
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::eventMouse(const Common::Event &ev) {
+ if (!_data || _data->index)
+ return;
+
+ // TODO move all the egg handling to inventory functions
+
+ getFlags()->mouseLeftClick = false;
+ getFlags()->shouldRedraw = false;
+ getFlags()->mouseRightClick = false;
+
+ if (ev.mouse.x < 608 || ev.mouse.y < 448 || ev.mouse.x >= 640 || ev.mouse.x >= 480) {
+
+ // Handle right button click
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ getSound()->removeFromQueue(kEntityTables0);
+ setStopped();
+
+ getGlobalTimer() ? _state = 0 : ++_state;
+
+ getFlags()->mouseRightClick = true;
+ }
+
+ if (_handleTimer) {
+ // Timer expired => show with full brightness
+ if (!getGlobalTimer())
+ getInventory()->drawEgg();
+
+ _handleTimer = false;
+ }
+
+ // Check hotspots
+ Scene *scene = getScenes()->get(getState()->scene);
+ SceneHotspot *hotspot = NULL;
+
+ if (!scene->checkHotSpot(ev.mouse, &hotspot)) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ } else {
+ _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor);
+
+ // Call player function
+ if (CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) {
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ CALL_FUNCTION1(_data->player, handleAction, (FightAction)hotspot->action);
+ } else {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ }
+ }
+ } else {
+ // Handle clicks on menu icon
+
+ if (!_handleTimer) {
+ // Timer expired => show with full brightness
+ if (!getGlobalTimer())
+ getInventory()->drawEgg();
+
+ _handleTimer = true;
+ }
+
+ // Stop fight if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ _handleTimer = false;
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndExit);
+ }
+
+ // Reset timer on right click
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ if (getGlobalTimer()) {
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+
+ setGlobalTimer(900);
+ }
+ }
+ }
+
+ getFlags()->shouldRedraw = true;
+}
+
+void Fight::eventTick(const Common::Event &ev) {
+ handleTick(ev, true);
+}
+
+void Fight::handleTick(const Common::Event &ev, bool isProcessing) {
+ // TODO move all the egg handling to inventory functions
+
+ // Blink egg
+ if (getGlobalTimer()) {
+ warning("Fight::handleMouseMove - egg blinking not implemented!");
+ }
+
+ if (!_data || _data->index)
+ return;
+
+ SceneHotspot *hotspot = NULL;
+ if (!getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot) || !CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ } else {
+ _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor);
+ }
+
+ CALL_FUNCTION0(_data->player, update);
+ CALL_FUNCTION0(_data->opponent, update);
+
+ // Draw sequences
+ if (!_data->isRunning)
+ return;
+
+ if (isProcessing)
+ getScenes()->drawFrames(true);
+
+ if (_data->index) {
+ // Set next sequence name index
+ _data->index--;
+ _data->sequences[_data->index] = loadSequence(_data->names[_data->index]);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+
+Fight::FightEndType Fight::setup(FightType type) {
+ if (_data)
+ error("Fight::setup - calling fight setup again while a fight is already in progress!");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Prepare UI & state
+ if (_state >= 5 && (type == kFightSalko || type == kFightVesna)) {
+ _state = 0;
+ return kFightEndWin;
+ }
+
+ getInventory()->showHourGlass();
+ // TODO events function
+ getFlags()->flag_0 = false;
+ getFlags()->mouseRightClick = false;
+ getEntities()->reset();
+
+ // Compute scene to use
+ SceneIndex sceneIndex;
+ switch(type) {
+ default:
+ sceneIndex = kSceneFightDefault;
+ break;
+
+ case kFightMilos:
+ sceneIndex = (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation3) ? kSceneFightMilos : kSceneFightMilosBedOpened;
+ break;
+
+ case kFightAnna:
+ sceneIndex = kSceneFightAnna;
+ break;
+
+ case kFightIvo:
+ sceneIndex = kSceneFightIvo;
+ break;
+
+ case kFightSalko:
+ sceneIndex = kSceneFightSalko;
+ break;
+
+ case kFightVesna:
+ sceneIndex = kSceneFightVesna;
+ break;
+ }
+
+ if (getFlags()->shouldRedraw) {
+ getFlags()->shouldRedraw = false;
+ askForRedraw();
+ //redrawScreen();
+ }
+
+ // Load the scene object
+ Scene *scene = getScenes()->get(sceneIndex);
+
+ // Update game entities and state
+ getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition;
+ getEntityData(kEntityPlayer)->location = scene->location;
+
+ getState()->scene = sceneIndex;
+
+ getFlags()->flag_3 = true;
+
+ // Draw the scene
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+ // FIXME move to start of fight?
+ askForRedraw();
+ redrawScreen();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Setup the fight
+ _data = new FightData;
+ loadData(type);
+
+ // Show opponents & egg button
+ Common::Event emptyEvent;
+ handleTick(emptyEvent, false);
+ getInventory()->drawEgg();
+
+ // Start fight
+ _endType = kFightEndLost;
+ while (_data->isRunning) {
+ if (_engine->handleEvents())
+ continue;
+
+ getSound()->updateQueue();
+ }
+
+ // Cleanup after fight is over
+ clearData();
+
+ return _endType;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Status
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::setStopped() {
+ if (_data)
+ _data->isRunning = false;
+}
+
+void Fight::bailout(FightEndType type) {
+ _state = 0;
+ _endType = type;
+ setStopped();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Cleanup
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::clearData() {
+ if (!_data)
+ return;
+
+ // Clear data
+ clearSequences(_data->player);
+ clearSequences(_data->opponent);
+
+ SAFE_DELETE(_data->player);
+ SAFE_DELETE(_data->opponent);
+
+ SAFE_DELETE(_data);
+
+ _engine->restoreEventHandlers();
+}
+
+void Fight::clearSequences(Fighter *combatant) const {
+ if (!combatant)
+ return;
+
+ // The original game resets the function pointers to default values, just before deleting the struct
+ getScenes()->removeAndRedraw(&combatant->frame, false);
+
+ // Free sequences
+ for (int i = 0; i < (int)combatant->sequences.size(); i++)
+ SAFE_DELETE(combatant->sequences[i]);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::setSequenceAndDraw(Fighter *combatant, uint32 sequenceIndex, FightSequenceType type) const {
+ if (combatant->sequences.size() < sequenceIndex)
+ return;
+
+ switch (type) {
+ default:
+ break;
+
+ case kFightSequenceType0:
+ if (combatant->sequenceIndex)
+ return;
+
+ combatant->sequence = combatant->sequences[sequenceIndex];
+ combatant->sequenceIndex = sequenceIndex;
+ draw(combatant);
+ break;
+
+ case kFightSequenceType1:
+ combatant->sequence = combatant->sequences[sequenceIndex];
+ combatant->sequenceIndex = sequenceIndex;
+ combatant->sequenceIndex2 = 0;
+ draw(combatant);
+ break;
+
+ case kFightSequenceType2:
+ combatant->sequenceIndex2 = sequenceIndex;
+ break;
+ }
+}
+
+void Fight::draw(Fighter *combatant) const {
+ getScenes()->removeAndRedraw(&combatant->frame, false);
+
+ combatant->frameIndex = 0;
+ combatant->field_24 = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Loading
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadData(FightType type) {
+ if (!_data)
+ error("Fight::loadData - invalid data!");
+
+ switch (type) {
+ default:
+ break;
+
+ case kFightMilos:
+ loadMilosPlayer();
+ loadMilosOpponent();
+ break;
+
+ case kFightAnna:
+ loadAnnaPlayer();
+ loadAnnaOpponent();
+ break;
+
+ case kFightIvo:
+ loadIvoPlayer();
+ loadIvoOpponent();
+ break;
+
+ case kFightSalko:
+ loadSalkoPlayer();
+ loadSalkoOpponent();
+ break;
+
+ case kFightVesna:
+ loadVesnaPlayer();
+ loadVesnaOpponent();
+ break;
+ }
+
+ if (!_data->player || !_data->opponent)
+ error("Fight::loadData - error loading fight data (type=%d)", type);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Start running the fight
+ _data->isRunning = true;
+
+ if (_state < 5) {
+ setSequenceAndDraw(_data->player, 0, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0);
+ goto end_load;
+ }
+
+ switch(type) {
+ default:
+ break;
+
+ case kFightMilos:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 4, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0);
+ break;
+
+ case kFightIvo:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 3, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 6, kFightSequenceType0);
+ break;
+
+ case kFightVesna:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 0, kFightSequenceType0);
+ setSequenceAndDraw(_data->player, 3, kFightSequenceType2);
+ setSequenceAndDraw(_data->opponent, 5, kFightSequenceType0);
+ break;
+ }
+
+end_load:
+ // Setup event handlers
+ _engine->backupEventHandlers();
+ SET_EVENT_HANDLERS(Fight, this);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Shared
+//////////////////////////////////////////////////////////////////////////
+void Fight::processFighter(Fighter *fighter) {
+ if (!_data)
+ error("Fight::processFighter - invalid data!");
+
+ if (!fighter->sequence) {
+ if (fighter->frame) {
+ getScenes()->removeFromQueue(fighter->frame);
+ getScenes()->setCoordinates(fighter->frame);
+ }
+ SAFE_DELETE(fighter->frame);
+ return;
+ }
+
+ if (fighter->sequence->count() <= fighter->frameIndex) {
+ switch(fighter->action) {
+ default:
+ break;
+
+ case kFightAction101:
+ setSequenceAndDraw(fighter, fighter->sequenceIndex2, kFightSequenceType1);
+ fighter->sequenceIndex2 = 0;
+ break;
+
+ case kFightActionResetFrame:
+ fighter->frameIndex = 0;
+ break;
+
+ case kFightAction103:
+ setSequenceAndDraw(fighter, 0, kFightSequenceType1);
+ CALL_FUNCTION1(fighter, handleAction, kFightAction101);
+ setSequenceAndDraw(fighter->opponent, 0, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction101);
+ CALL_FUNCTION0(fighter->opponent, update);
+ break;
+
+ case kFightActionWin:
+ bailout(kFightEndWin);
+ break;
+
+ case kFightActionLost:
+ bailout(kFightEndLost);
+ break;
+ }
+ }
+
+ if (_data->isRunning) {
+
+ // Get the current sequence frame
+ SequenceFrame *frame = new SequenceFrame(fighter->sequence, (uint16)fighter->frameIndex);
+ frame->getInfo()->location = 1;
+
+ if (fighter->frame == frame) {
+ delete frame;
+ return;
+ }
+
+ getSound()->playFightSound(frame->getInfo()->soundAction, frame->getInfo()->field_31);
+
+ // Add current frame to queue and advance
+ getScenes()->addToQueue(frame);
+ fighter->frameIndex++;
+
+ if (fighter->frame) {
+ getScenes()->removeFromQueue(fighter->frame);
+
+ if (!frame->getInfo()->field_2E)
+ getScenes()->setCoordinates(fighter->frame);
+ }
+
+ // Replace by new frame
+ delete fighter->frame;
+ fighter->frame = frame;
+ }
+}
+
+void Fight::handleAction(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ return;
+
+ case kFightAction101:
+ break;
+
+ case kFightActionResetFrame:
+ fighter->countdown--;
+ break;
+
+ case kFightAction103:
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+
+ case kFightActionWin:
+ _endType = kFightEndWin;
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+
+ case kFightActionLost:
+ _endType = kFightEndLost;
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+ }
+
+ // Update action
+ fighter->action = action;
+}
+
+bool Fight::canInteract(Fighter const *fighter, FightAction /*= (FightAction)0*/ ) {
+ return (fighter->action == kFightAction101 && !fighter->sequenceIndex);
+}
+
+void Fight::update(Fighter *fighter) {
+
+ processFighter(fighter);
+
+ if (fighter->frame)
+ fighter->frame->getInfo()->location = (fighter->action == kFightActionResetFrame ? 2 : 0);
+}
+
+void Fight::updateOpponent(Fighter *fighter) {
+
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ processFighter(opponent);
+
+ if (opponent->field_38 && !opponent->sequenceIndex)
+ opponent->field_38--;
+
+ if (fighter->frame)
+ fighter->frame->getInfo()->location = 1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Milos
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadMilosPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Milos)
+
+ _data->player->sequences.push_back(loadSequence("2001cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdl.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2001csgr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001csgl.seq"));
+ _data->player->sequences.push_back(loadSequence("2001dbk.seq"));
+}
+
+void Fight::loadMilosOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Milos)
+
+ _data->opponent->sequences.push_back(loadSequence("2001or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001okl.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001okm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001dbk.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001wbk.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS027", SoundManager::kFlagDefault);
+
+ _data->opponent->field_38 = 35;
+}
+
+void Fight::handleActionMilos(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 6, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 3, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 6, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction128:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4) || fighter->opponent->sequenceIndex != 1) {
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ setSequenceAndDraw(fighter, rnd(3) + 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+ }
+ } else {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+ }
+}
+
+void Fight::updateMilos(Fighter *fighter) {
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ // Draw sequences
+ if (fighter->opponent->countdown <= 0) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1);
+
+ getSound()->removeFromQueue(kEntityTables0);
+ getSound()->playSound(kEntityTrain, "MUS029", SoundManager::kFlagDefault);
+
+ CALL_FUNCTION1(fighter, handleAction, kFightActionWin);
+ }
+
+ if (fighter->sequenceIndex == 4) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction4);
+ _endType = kFightEndLost;
+ }
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractMilos(Fighter const *fighter, FightAction action) {
+ if (!_data)
+ error("Fight::canInteractMilos - invalid data!");
+
+ if (action != kFightAction128
+ || _data->player->sequenceIndex != 1
+ || !fighter->frame
+ || CHECK_SEQUENCE2(fighter, 4)
+ || fighter->opponent->sequenceIndex != 1) {
+ return canInteract(fighter);
+ }
+
+ _engine->getCursor()->setStyle(kCursorHand);
+
+ return true;
+}
+
+void Fight::handleOpponentActionMilos(Fighter *fighter, FightAction action) {
+ if (action == kFightAction4) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ } else {
+ if (action != kFightAction131)
+ handleAction(fighter, action);
+ }
+}
+
+void Fight::updateOpponentMilos(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType1);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ } else {
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ }
+
+ // Update field_38
+ if (opponent->opponent->field_34 < 5)
+ opponent->field_38 = 6 * (5 - opponent->opponent->field_34);
+ else
+ opponent->field_38 = 0;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Anna
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadAnnaPlayer() {
+ if (!_data)
+ error("Fight::loadAnnaPlayer - invalid data!");
+
+ // Special case: we are using some shared functions directly
+ _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleActionAnna);
+ _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update);
+ _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+ _data->player->sequences.push_back(loadSequence("2002cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdl.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2002lbk.seq"));
+}
+
+void Fight::loadAnnaOpponent() {
+ if (!_data)
+ error("Fight::loadAnnaOpponent - invalid data!");
+
+ // Special case: we are using some shared functions directly
+ _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction);
+ _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponentAnna);
+ _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+ _data->opponent->sequences.push_back(loadSequence("2002or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okml.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okm.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS030", SoundManager::kFlagDefault);
+
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionAnna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if ((fighter->sequenceIndex != 1 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction3:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 1) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction128:
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+ break;
+ }
+
+ if (fighter->field_34 > 4) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+ }
+}
+
+void Fight::updateOpponentAnna(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(6)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = (int32)rnd(15);
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 3)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Ivo
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadIvoPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Ivo)
+
+ _data->player->sequences.push_back(loadSequence("2003cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003car.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cal.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2003chr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003chl.seq"));
+ _data->player->sequences.push_back(loadSequence("2003ckr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003lbk.seq"));
+ _data->player->sequences.push_back(loadSequence("2003fbk.seq"));
+
+ _data->player->countdown = 5;
+}
+
+void Fight::loadIvoOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Ivo)
+
+ _data->opponent->sequences.push_back(loadSequence("2003or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003odm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003okl.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003okj.seq"));
+ _data->opponent->sequences.push_back(loadSequence("blank.seq"));
+ _data->opponent->sequences.push_back(loadSequence("csdr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003l.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS032", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 5;
+ _data->opponent->field_38 = 15;
+}
+
+void Fight::handleActionIvo(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+ break;
+
+ case kFightAction129:
+ setSequenceAndDraw(fighter, (fighter->opponent->countdown > 1) ? 4 : 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0);
+ break;
+
+ case kFightAction130:
+ setSequenceAndDraw(fighter, 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0);
+ break;
+ }
+}
+
+void Fight::updateIvo(Fighter *fighter) {
+
+ if ((fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4) && !fighter->frameIndex)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction131);
+
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ // Draw sequences
+ if (fighter->opponent->countdown <= 0) {
+ setSequenceAndDraw(fighter, 9, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 8, kFightSequenceType1);
+ getSound()->removeFromQueue(kEntityTables0);
+
+ CALL_FUNCTION1(fighter, handleAction, kFightActionWin);
+ return;
+ }
+
+ if (fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4)
+ CALL_FUNCTION1(fighter->opponent, handleAction, (FightAction)fighter->sequenceIndex);
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractIvo(Fighter const *fighter, FightAction action) {
+ if (action == kFightAction129 || action == kFightAction130)
+ return (fighter->sequenceIndex >= 8);
+
+ return canInteract(fighter);
+}
+
+void Fight::handleOpponentActionIvo(Fighter *fighter, FightAction action) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ break;
+
+ case kFightAction3:
+ if ((opponent->sequenceIndex != 1 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) {
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 6, kFightSequenceType1);
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103);
+ }
+ break;
+
+ case kFightAction4:
+ if ((opponent->sequenceIndex != 2 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) {
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 5, kFightSequenceType1);
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103);
+ }
+ break;
+
+ case kFightAction131:
+ if (opponent->sequenceIndex)
+ break;
+
+ if (rnd(100) <= (unsigned int)(opponent->countdown > 2 ? 60 : 75)) {
+ setSequenceAndDraw(opponent, 3 , kFightSequenceType1);
+ if (opponent->opponent->sequenceIndex == 4)
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ }
+ break;
+ }
+}
+
+void Fight::updateOpponentIvo(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 0, kFightSequenceType2);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 0, kFightSequenceType1);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = 3 * opponent->countdown + (int32)rnd(10);
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+
+ if (opponent->opponent->countdown <= 0) {
+ setSequenceAndDraw(opponent, 7, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 8, kFightSequenceType1);
+ getSound()->removeFromQueue(kEntityTables0);
+
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightActionWin);
+
+ return;
+ }
+
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Salko
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadSalkoPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Salko)
+
+ _data->player->sequences.push_back(loadSequence("2004cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2004cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2004chj.seq"));
+ _data->player->sequences.push_back(loadSequence("2004bk.seq"));
+
+ _data->player->countdown = 2;
+}
+
+void Fight::loadSalkoOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Salko)
+
+ _data->opponent->sequences.push_back(loadSequence("2004or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004ohm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("blank.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS035", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 3;
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionSalko(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ case kFightAction2:
+ if (fighter->sequenceIndex != 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ fighter->field_34 = 0;
+
+ setSequenceAndDraw(fighter, 3, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, (action == kFightAction1 ? 3 : 4), kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+
+ if (action == kFightAction2)
+ fighter->countdown= 0;
+
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction5:
+ if (fighter->sequenceIndex != 3) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ fighter->field_34 = 0;
+ break;
+
+ case kFightAction131:
+ setSequenceAndDraw(fighter, 2, (fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0));
+ break;
+ }
+}
+
+void Fight::updateSalko(Fighter *fighter) {
+ update(fighter);
+
+ // The original doesn't check for currentSequence2 != NULL (might not happen when everything is working properly, but crashes with our current implementation)
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ if (fighter->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+
+ return;
+ }
+
+ if (fighter->sequenceIndex == 2)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction2);
+ }
+}
+
+bool Fight::canInteractSalko(Fighter const *fighter, FightAction action) {
+ if (action == kFightAction131) {
+ if (fighter->sequenceIndex == 1) {
+ if (fighter->opponent->countdown <= 0)
+ _engine->getCursor()->setStyle(kCursorHand);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ return canInteract(fighter);
+}
+
+void Fight::handleOpponentActionSalko(Fighter *fighter, FightAction action) {
+ if (action == kFightAction2) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ } else {
+ handleAction(fighter, action);
+ }
+}
+
+void Fight::updateOpponentSalko(Fighter *fighter) {
+ // This is an opponent struct
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+
+ // Update field_38
+ opponent->field_38 = 4 * opponent->countdown;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndLost);
+
+ // Stop processing
+ return;
+ }
+
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Vesna
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadVesnaPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Vesna)
+
+ _data->player->sequences.push_back(loadSequence("2005cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cbr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005bk.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cdm1.seq"));
+ _data->player->sequences.push_back(loadSequence("2005chl.seq"));
+}
+
+void Fight::loadVesnaOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Vesna)
+
+ _data->opponent->sequences.push_back(loadSequence("2005or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005okml.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005odm1.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005csbm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oam4.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS038", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 4;
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionVesna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if (fighter->sequenceIndex != 2) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction5:
+ if (fighter->sequenceIndex != 3) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ if (fighter->sequenceIndex == 1 && fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ } else {
+ setSequenceAndDraw(fighter, (fighter->opponent->sequenceIndex == 5) ? 3 : 1, kFightSequenceType0);
+ }
+ break;
+
+ case kFightAction132:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+
+ if (fighter->field_34 > 10) {
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType2);
+ fighter->opponent->countdown = 1;
+ fighter->field_34 = 0;
+ }
+}
+
+void Fight::updateVesna(Fighter *fighter) {
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ if (fighter->sequenceIndex == 3)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction3);
+
+ if (fighter->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+ return;
+ }
+
+ if (fighter->sequenceIndex == 5)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction5);
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractVesna(Fighter const *fighter, FightAction action) {
+ if (action != kFightAction128)
+ return canInteract(fighter);
+
+ if (fighter->sequenceIndex != 1) {
+
+ if (fighter->opponent->sequenceIndex == 5) {
+ _engine->getCursor()->setStyle(kCursorDown);
+ return true;
+ }
+
+ return canInteract(fighter);
+ }
+
+ if (fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ _engine->getCursor()->setStyle(kCursorPunchLeft);
+ return true;
+ }
+
+ return false;
+}
+
+void Fight::handleOpponentActionVesna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ break;
+
+ case kFightAction3:
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ break;
+
+ case kFightAction5:
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ if (fighter->countdown <= 1)
+ fighter->countdown = 1;
+ break;
+
+ case kFightAction131:
+ break;
+ }
+}
+
+void Fight::updateOpponentVesna(Fighter *fighter) {
+ // This is an opponent struct
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 == 1) {
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ } else {
+ switch (rnd(6)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = 4 * opponent->countdown;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 5)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+
+ switch (opponent->sequenceIndex) {
+ default:
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType1);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 4, kFightSequenceType1);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ break;
+ }
+
+ setSequenceAndDraw(opponent->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ CALL_FUNCTION0(opponent->opponent, update);
+ CALL_FUNCTION0(opponent, update);
+
+ getSound()->removeFromQueue(kEntityTables0);
+
+ // Stop processing
+ return;
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/fight.h b/engines/lastexpress/game/fight.h
new file mode 100644
index 0000000000..4484017184
--- /dev/null
+++ b/engines/lastexpress/game/fight.h
@@ -0,0 +1,269 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_FIGHT_H
+#define LASTEXPRESS_FIGHT_H
+
+/*
+ Fight structure
+ ---------------
+ uint32 {4} - player struct
+ uint32 {4} - opponent struct
+ uint32 {4} - hasLost flag
+
+ byte {1} - isRunning
+
+ Fight participant structure
+ ---------------------------
+ uint32 {4} - function pointer
+ uint32 {4} - pointer to fight structure
+ uint32 {4} - pointer to opponent (fight participant structure)
+ uint32 {4} - array of sequences
+ uint32 {4} - number of sequences
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint16 {2} - ??
+ uint16 {2} - ?? - only for opponent structure
+ uint32 {4} - ?? - only for opponent structure
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/array.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+class SequenceFrame;
+
+//////////////////////////////////////////////////////////////////////////
+// TODO : objectify!
+class Fight : public EventHandler {
+public:
+ enum FightEndType {
+ kFightEndWin = 0,
+ kFightEndLost = 1,
+ kFightEndExit = 2
+ };
+
+ Fight(LastExpressEngine *engine);
+ ~Fight();
+
+ FightEndType setup(FightType type);
+
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ void setStopped();
+ void resetState() { _state = 0; }
+
+private:
+ enum FightSequenceType {
+ kFightSequenceType0 = 0,
+ kFightSequenceType1 = 1,
+ kFightSequenceType2 = 2
+ };
+
+ enum FightAction {
+ kFightAction1 = 1,
+ kFightAction2 = 2,
+ kFightAction3 = 3,
+ kFightAction4 = 4,
+ kFightAction5 = 5,
+ kFightAction101 = 101,
+ kFightActionResetFrame = 102,
+ kFightAction103 = 103,
+ kFightActionWin = 104,
+ kFightActionLost = 105,
+ kFightAction128 = 128,
+ kFightAction129 = 129,
+ kFightAction130 = 130,
+ kFightAction131 = 131,
+ kFightAction132 = 132
+ };
+
+ struct Fighter {
+ Common::Functor2<Fighter *, FightAction, void> *handleAction;
+ Common::Functor1<Fighter *, void> *update;
+ Common::Functor2<Fighter const *, FightAction, bool> *canInteract;
+ Fighter *opponent;
+ Common::Array<Sequence *> sequences;
+ uint32 sequenceIndex;
+ Sequence *sequence;
+ SequenceFrame *frame;
+ uint32 frameIndex;
+ uint32 field_24;
+ FightAction action;
+ uint32 sequenceIndex2;
+ int32 countdown; // countdown before loosing ?
+ uint32 field_34;
+
+ Fighter() {
+ handleAction = NULL;
+ update = NULL;
+ canInteract = NULL;
+
+ opponent = NULL;
+
+ sequenceIndex = 0;
+ sequence = NULL;
+ frame = NULL;
+ frameIndex = 0;
+
+ field_24 = 0;
+
+ action = kFightAction101;
+ sequenceIndex2 = 0;
+
+ countdown = 1;
+
+ field_34 = 0;
+ }
+ };
+
+ // Opponent struct
+ struct Opponent : Fighter {
+ int32 field_38;
+
+ Opponent() : Fighter() {
+ field_38 = 0;
+ }
+ };
+
+ struct FightData {
+ Fighter *player;
+ Opponent *opponent;
+ int32 index;
+
+ Sequence *sequences[20];
+ Common::String names[20];
+
+ bool isRunning;
+
+ FightData() {
+ player = new Fighter();
+ opponent = new Opponent();
+
+ // Set opponents
+ player->opponent = opponent;
+ opponent->opponent = player;
+
+ index = 0;
+
+ isRunning = false;
+ }
+ };
+
+ LastExpressEngine *_engine;
+ FightData *_data;
+ FightEndType _endType;
+ int _state;
+
+ bool _handleTimer;
+
+ // Events
+ void handleTick(const Common::Event &ev, bool unknown);
+
+ // State
+ void bailout(FightEndType type);
+
+
+ // Drawing
+ void setSequenceAndDraw(Fighter *fighter, uint32 sequenceIndex, FightSequenceType type) const;
+ void draw(Fighter *fighter) const;
+
+ // Cleanup
+ void clearData();
+ void clearSequences(Fighter *fighter) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Loading
+ void loadData(FightType type);
+
+ // Shared
+ void processFighter(Fighter *fighter);
+
+ // Default functions
+ void handleAction(Fighter *fighter, FightAction action);
+ void update(Fighter *fighter);
+ bool canInteract(Fighter const *fighter, FightAction = (FightAction)0);
+ void updateOpponent(Fighter *fighter);
+
+ // Milos
+ void loadMilosPlayer();
+ void loadMilosOpponent();
+ void handleActionMilos(Fighter *fighter, FightAction action);
+ void updateMilos(Fighter *fighter);
+ bool canInteractMilos(Fighter const *fighter, FightAction action);
+ void handleOpponentActionMilos(Fighter *fighter, FightAction action);
+ void updateOpponentMilos(Fighter *fighter);
+
+ // Anna
+ void loadAnnaPlayer();
+ void loadAnnaOpponent();
+ void handleActionAnna(Fighter *fighter, FightAction action);
+ void updateOpponentAnna(Fighter *fighter);
+
+ // Ivo
+ void loadIvoPlayer();
+ void loadIvoOpponent();
+ void handleActionIvo(Fighter *fighter, FightAction action);
+ void updateIvo(Fighter *fighter);
+ bool canInteractIvo(Fighter const *fighter, FightAction action);
+ void handleOpponentActionIvo(Fighter *fighter, FightAction action);
+ void updateOpponentIvo(Fighter *fighter);
+
+ // Salko
+ void loadSalkoPlayer();
+ void loadSalkoOpponent();
+ void handleActionSalko(Fighter *fighter, FightAction action);
+ void updateSalko(Fighter *fighter);
+ bool canInteractSalko(Fighter const *fighter, FightAction action);
+ void handleOpponentActionSalko(Fighter *fighter, FightAction action);
+ void updateOpponentSalko(Fighter *fighter);
+
+ // Vesna
+ void loadVesnaPlayer();
+ void loadVesnaOpponent();
+ void handleActionVesna(Fighter *fighter, FightAction action);
+ void updateVesna(Fighter *fighter);
+ bool canInteractVesna(Fighter const *fighter, FightAction action);
+ void handleOpponentActionVesna(Fighter *fighter, FightAction action);
+ void updateOpponentVesna(Fighter *fighter);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FIGHT_H
diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp
new file mode 100644
index 0000000000..ae9aca56b6
--- /dev/null
+++ b/engines/lastexpress/game/inventory.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/inventory.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+
+#define drawItem(x, y, index, brightness) { Icon icon((CursorStyle)(index)); icon.setPosition(x, y); icon.setBrightness(brightness); _engine->getGraphicsManager()->draw(&icon, GraphicsManager::kBackgroundInventory); }
+
+namespace LastExpress {
+
+Inventory::Inventory(LastExpressEngine *engine) : _engine(engine), _selectedItem(kItemNone), _highlightedItem(kItemNone), _opened(false), _visible(false),
+ _showingHourGlass(false), _blinkingEgg(false), _blinkingTime(0), _blinkingInterval(_defaultBlinkingInterval), _blinkingBrightness(100),
+ _flagUseMagnifier(false), _flag1(false), _flag2(false), _flagEggHightlighted(false), _itemScene(NULL) {
+
+ _inventoryRect = Common::Rect(0, 0, 32, 32);
+ _menuRect = Common::Rect(608, 448, 640, 480);
+ _selectedRect = Common::Rect(44, 0, 76, 32);
+
+ init();
+}
+
+Inventory::~Inventory() {
+ _itemScene = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Inventory handling
+//////////////////////////////////////////////////////////////////////////
+
+// Initialize inventory contents
+void Inventory::init() {
+ // ID
+ _entries[kItemMatchBox].cursor = kCursorMatchBox;
+ _entries[kItemTelegram].cursor = kCursorTelegram;
+ _entries[kItemPassengerList].cursor = kCursorPassengerList;
+ _entries[kItemArticle].cursor = kCursorArticle;
+ _entries[kItemScarf].cursor = kCursorScarf;
+ _entries[kItemPaper].cursor = kCursorPaper;
+ _entries[kItemParchemin].cursor = kCursorParchemin;
+ _entries[kItemMatch].cursor = kCursorMatch;
+ _entries[kItemWhistle].cursor = kCursorWhistle;
+ _entries[kItemKey].cursor = kCursorKey;
+ _entries[kItemBomb].cursor = kCursorBomb;
+ _entries[kItemFirebird].cursor = kCursorFirebird;
+ _entries[kItemBriefcase].cursor = kCursorBriefcase;
+ _entries[kItemCorpse].cursor = kCursorCorpse;
+
+ // Selectable
+ _entries[kItemMatchBox].isSelectable = true;
+ _entries[kItemMatch].isSelectable = true;
+ _entries[kItemTelegram].isSelectable = true;
+ _entries[kItemWhistle].isSelectable = true;
+ _entries[kItemKey].isSelectable = true;
+ _entries[kItemFirebird].isSelectable = true;
+ _entries[kItemBriefcase].isSelectable = true;
+ _entries[kItemCorpse].isSelectable = true;
+ _entries[kItemPassengerList].isSelectable = true;
+
+ // Auto selection
+ _entries[kItem2].manualSelect = false;
+ _entries[kItem3].manualSelect = false;
+ _entries[kItem5].manualSelect = false;
+ _entries[kItem7].manualSelect = false;
+ _entries[kItem9].manualSelect = false;
+ _entries[kItem11].manualSelect = false;
+ _entries[kItemBeetle].manualSelect = false;
+ _entries[kItem17].manualSelect = false;
+ _entries[kItemFirebird].manualSelect = false;
+ _entries[kItemBriefcase].manualSelect = false;
+ _entries[kItemCorpse].manualSelect = false;
+ _entries[kItemGreenJacket].manualSelect = false;
+ _entries[kItem22].manualSelect = false;
+
+ // Scene
+ _entries[kItemMatchBox].scene = kSceneMatchbox;
+ _entries[kItemTelegram].scene = kSceneTelegram;
+ _entries[kItemPassengerList].scene = kScenePassengerList;
+ _entries[kItemScarf].scene = kSceneScarf;
+ _entries[kItemParchemin].scene = kSceneParchemin;
+ _entries[kItemArticle].scene = kSceneArticle;
+ _entries[kItemPaper].scene = kScenePaper;
+ _entries[kItemFirebird].scene = kSceneFirebird;
+ _entries[kItemBriefcase].scene = kSceneBriefcase;
+
+ // Has item
+ _entries[kItemTelegram].isPresent = true;
+ _entries[kItemArticle].isPresent = true;
+
+ _selectedItem = kItemNone;
+}
+
+// FIXME we need to draw cursors with full background opacity so that whatever is in the background is erased
+// this saved us clearing some part of the background when switching between states
+
+// TODO if we draw inventory objects on screen, we need to load a new scene.
+// Signal that the inventory has taken over the screen and stop processing mouse events after we have been called
+bool Inventory::handleMouseEvent(const Common::Event &ev) {
+
+ // Do not show inventory when on the menu screen
+ if (getMenu()->isShown() || !_visible)
+ return false;
+
+ // Flag to know whether to restore the current cursor or not
+ bool insideInventory = false;
+
+ // Egg (menu)
+ if (_menuRect.contains(ev.mouse)) {
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ // If clicked, show the menu
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ getSound()->playSound(kEntityPlayer, "LIB039");
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+
+ // TODO can we return directly or do we need to make sure the state will be "valid" when we come back from the menu
+ return true;
+ } else {
+ // Highlight if needed
+ if (_highlightedItem != getMenu()->getGameId() + 39) {
+ _highlightedItem = (InventoryItem)(getMenu()->getGameId() + 39);
+ drawItem(608, 448, _highlightedItem, 100)
+
+ askForRedraw();
+ }
+ }
+ } else {
+ // remove highlight if needed
+ if (_highlightedItem == getMenu()->getGameId() + 39) {
+ drawItem(608, 448, _highlightedItem, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ // Portrait (inventory)
+ if (_inventoryRect.contains(ev.mouse)) {
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ // If clicked, show pressed state and display inventory
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ open();
+ } else {
+ // Highlight if needed
+ if (_highlightedItem != (InventoryItem)getProgress().portrait && !_opened) {
+ _highlightedItem = (InventoryItem)getProgress().portrait;
+ drawItem(0, 0, getProgress().portrait, 100)
+
+ askForRedraw();
+ }
+ }
+ } else {
+ // remove highlight if needed
+ if (_highlightedItem == (InventoryItem)getProgress().portrait && !_opened) {
+ drawItem(0, 0, getProgress().portrait, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ // If the inventory is open, check all items rect to see if we need to highlight one / handle click
+ if (_opened) {
+
+ // Always show normal cursor when the inventory is opened
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ bool selected = false;
+
+ // Iterate over items
+ int16 y = 44;
+ for (int i = 1; i < 32; i++) {
+ if (!hasItem((InventoryItem)i))
+ continue;
+
+ if (Common::Rect(0, y, 32, 32 + y).contains(ev.mouse)) {
+
+ // If released with an item highlighted, show this item
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ if (_entries[i].isSelectable) {
+ selected = true;
+ _selectedItem = (InventoryItem)i;
+ drawItem(44, 0, get(_selectedItem)->cursor, 100)
+ }
+
+ examine((InventoryItem)i);
+ break;
+ } else {
+ if (_highlightedItem != i) {
+ drawItem(0, y, _entries[i].cursor, 100)
+ _highlightedItem = (InventoryItem)i;
+ askForRedraw();
+ }
+ }
+ } else {
+ // Remove highlight if necessary
+ if (_highlightedItem == i) {
+ drawItem(0, y, _entries[i].cursor, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ y += 40;
+ }
+
+ // Right button is released: we need to close the inventory
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ // Not on a selectable item: unselect the current item
+ if (!selected)
+ unselectItem();
+
+ close();
+ }
+ }
+
+ // Selected item
+ if (_selectedItem != kItemNone && _selectedRect.contains(ev.mouse)) {
+ insideInventory = true;
+
+ // Show magnifier icon
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ examine((InventoryItem)_selectedItem);
+ }
+ }
+
+ // If the egg is blinking, refresh
+ if (_blinkingEgg)
+ drawEgg();
+
+ // Restore cursor
+ //if (!insideInventory)
+ // _engine->getCursor()->setStyle(getLogic()->getCursorStyle());
+
+ return insideInventory;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// UI
+//////////////////////////////////////////////////////////////////////////
+void Inventory::show() {
+ clearBg(GraphicsManager::kBackgroundInventory);
+ askForRedraw();
+
+ // Show portrait (first draw, cannot be highlighted)
+ drawItem(0, 0, getProgress().portrait, 50)
+
+ // Show selected item
+ if (_selectedItem != kItemNone)
+ drawItem(44, 0, _selectedItem, 100)
+
+ drawEgg();
+}
+
+void Inventory::setPortrait(InventoryItem item) const {
+ getProgress().portrait = item;
+ drawItem(0, 0, getProgress().portrait, 50);
+}
+
+void Inventory::blinkEgg(bool enabled) {
+ _blinkingEgg = enabled;
+
+ // Reset state
+ _showingHourGlass = false;
+
+ // Show egg at full brightness for first step if blinking
+ if (_blinkingEgg)
+ drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness)
+ else {
+ // Reset values
+ _blinkingBrightness = 100;
+ _blinkingInterval = _defaultBlinkingInterval;
+ drawItem(608, 448, getMenu()->getGameId() + 39, 50) // normal egg state
+ }
+
+ askForRedraw();
+}
+
+void Inventory::showHourGlass() const{
+ if (!getFlags()->flag_5) {
+ drawItem(608, 448, kCursorHourGlass, 100);
+ }
+
+ askForRedraw();
+
+ getFlags()->shouldDrawEggOrHourGlass = true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Items
+//////////////////////////////////////////////////////////////////////////
+Inventory::InventoryEntry *Inventory::get(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ error("Inventory::getEntry: Invalid inventory item!");
+
+ return &_entries[item];
+}
+
+void Inventory::addItem(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ get(item)->isPresent = true;
+ get(item)->location = kObjectLocationNone;
+
+ // Auto-select item if necessary
+ if (get(item)->cursor && !get(item)->manualSelect) {
+ _selectedItem = (InventoryItem)get(item)->cursor;
+ drawItem(44, 0, _selectedItem, 100)
+ askForRedraw();
+ }
+}
+
+void Inventory::removeItem(InventoryItem item, ObjectLocation newLocation) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ get(item)->isPresent = false;
+ get(item)->location = newLocation;
+
+ if (get(item)->cursor == (CursorStyle)_selectedItem) {
+ _selectedItem = kItemNone;
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32));
+ askForRedraw();
+ }
+}
+
+bool Inventory::hasItem(InventoryItem item) {
+ if (get(item)->isPresent && item < kPortraitOriginal)
+ return true;
+
+ return false;
+}
+
+void Inventory::selectItem(InventoryItem item) {
+ _selectedItem = item;
+
+ drawItem(44, 0, get(_selectedItem)->cursor, 100)
+ askForRedraw();
+}
+
+void Inventory::unselectItem() {
+ _selectedItem = kItemNone;
+
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32));
+ askForRedraw();
+}
+
+void Inventory::setLocationAndProcess(InventoryItem item, ObjectLocation location) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ if (get(item)->location == location)
+ return;
+
+ get(item)->location = location;
+
+ if (isItemSceneParameter(item) && !getFlags()->flag_0)
+ getScenes()->processScene();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void Inventory::saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(_entries); i++)
+ _entries[i].saveLoadWithSerializer(s);
+}
+
+void Inventory::saveSelectedItem(Common::Serializer &s) {
+ s.syncAsUint32LE(_selectedItem);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String Inventory::toString() {
+ Common::String ret = "";
+
+ for (int i = 0; i < kPortraitOriginal; i++)
+ ret += Common::String::format("%d : %s\n", i, _entries[i].toString().c_str());
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private methods
+//////////////////////////////////////////////////////////////////////////
+InventoryItem Inventory::getFirstExaminableItem() const {
+
+ int index = 0;
+ InventoryEntry entry = _entries[index];
+ while (!entry.isPresent || !entry.cursor || entry.manualSelect) {
+ index++;
+ entry = _entries[index];
+
+ if (index >= kPortraitOriginal)
+ return kItemNone;
+ }
+
+ return (InventoryItem)index;
+}
+
+bool Inventory::isItemSceneParameter(InventoryItem item) const {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ switch(scene->type) {
+ default:
+ return false;
+
+ case Scene::kTypeItem:
+ if (scene->param1 == item)
+ return true;
+ break;
+
+ case Scene::kTypeItem2:
+ if (scene->param1 == item || scene->param2 == item)
+ return true;
+ break;
+
+ case Scene::kTypeObjectItem:
+ if (scene->param2 == item)
+ return true;
+ break;
+
+ case Scene::kTypeItem3:
+ if (scene->param1 == item || scene->param2 == item || scene->param3 == item)
+ return true;
+ break;
+
+ case Scene::kTypeCompartmentsItem:
+ if (scene->param2 == item)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+// Examine an inventory item
+void Inventory::examine(InventoryItem item) {
+ SceneIndex index = get(item)->scene;
+ if (!index)
+ return;
+
+ /*if (!getState()->sceneUseBackup ||
+ (getState()->sceneBackup2 && getFirstExaminableItem() == _selectedItem))
+ flag = 1;*/
+
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneBackup = getState()->scene;
+ getState()->sceneUseBackup = true;
+
+ getScenes()->loadScene(index);
+ } else {
+
+ if (!getState()->sceneBackup2)
+ return;
+
+ if (getFirstExaminableItem() == _selectedItem) {
+ index = getState()->sceneBackup2;
+ getState()->sceneBackup2 = kSceneNone;
+ getScenes()->loadScene(index);
+ }
+ }
+}
+
+// FIXME: see different callers and adjust
+// - draw with different brightness if mousing over
+void Inventory::drawEgg() const {
+ if (!getFlags()->flag_5)
+ drawItem(608, 448, getMenu()->getGameId() + 39, 50)
+
+ getFlags()->shouldDrawEggOrHourGlass = false;
+}
+
+// Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit.
+void Inventory::drawBlinkingEgg() {
+
+ warning("Inventory::drawEgg - blinking not implemented!");
+
+ //// TODO show egg (with or without mouseover)
+
+ //// Play timer sound
+ //if (getGlobalTimer() < 90) {
+ // if (getGlobalTimer() + ticks >= 90)
+ // getSound()->playSoundWithSubtitles("TIMER.SND", 50331664, kEntityPlayer);
+
+ // if (getSound()->isBuffered("TIMER"))
+ // setGlobalTimer(0);
+ //}
+
+ //// Restore egg to standard brightness
+ //if (!getGlobalTimer()) {
+ //
+ //}
+
+
+ //drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness)
+
+ //// TODO if delta time > _blinkingInterval, update egg & ask for redraw then adjust blinking time and remaining time
+ //
+
+ //// Reset values and stop blinking
+ //if (_blinkingTime == 0)
+ // blinkEgg(false);
+
+ askForRedraw();
+}
+
+// Close inventory: clear items and reset icon
+void Inventory::open() {
+ _opened = true;
+
+ // Show selected state
+ drawItem(0, 0, getProgress().portrait + 1, 100)
+
+ int16 y = 44;
+
+ // Iterate over items
+ for (uint i = 1; i < 32; i++) {
+ if (_entries[i].isPresent) {
+ drawItem(0, y, _entries[i].cursor, 50)
+ y += 40;
+ }
+ }
+
+ askForRedraw();
+}
+
+// Close inventory: clear items and reset icon
+void Inventory::close() {
+ _opened = false;
+
+ // Fallback to unselected state
+ drawItem(0, 0, getProgress().portrait, 100)
+
+ // Erase rectangle for all inventory items
+ int count = 0;
+ for (uint i = 1; i < 32; i++) {
+ if (_entries[i].isPresent) {
+ count++;
+ }
+ }
+
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(0, 44, 32, (int16)(44 + 44 * count)));
+
+ askForRedraw();
+}
+
+Common::Rect Inventory::getItemRect(int16 index) const{
+ return Common::Rect(0, (int16)((32 + 12) * (index + 1)), 32, (int16)((32 + 12) * (index + 2))); // space between items = 12px
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/inventory.h b/engines/lastexpress/game/inventory.h
new file mode 100644
index 0000000000..bae6c8d261
--- /dev/null
+++ b/engines/lastexpress/game/inventory.h
@@ -0,0 +1,180 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_INVENTORY_H
+#define LASTEXPRESS_INVENTORY_H
+
+/*
+ Inventory entry (32 entries)
+ ----------------------------
+
+ byte {1} - Item ID (set to 0 for "undefined" items)
+ byte {1} - Scene ID
+ byte {1} - ??
+ byte {1} - Selectable (1 if item is selectable, 0 otherwise)
+ byte {1} - Is item in inventory (set to 1 for telegram and article)
+ byte {1} - Auto selection (1 for no auto selection, 0 otherwise)
+ byte {1} - Location
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/events.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Scene;
+
+class Inventory : Common::Serializable, public EventHandler {
+public:
+
+ // Entry
+ struct InventoryEntry : Common::Serializable {
+ CursorStyle cursor;
+ SceneIndex scene;
+ byte field_2;
+ bool isSelectable;
+ bool isPresent;
+ bool manualSelect;
+ ObjectLocation location;
+
+ InventoryEntry() {
+ cursor = kCursorNormal;
+ scene = kSceneNone;
+ field_2 = 0;
+ isSelectable = false;
+ isPresent = false;
+ manualSelect = true;
+ location = kObjectLocationNone;
+ }
+
+ Common::String toString() {
+ return Common::String::format("{ %d - %d - %d - %d - %d - %d - %d }", cursor, scene, field_2, isSelectable, isPresent, manualSelect, location);
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsByte(cursor);
+ s.syncAsByte(scene);
+ s.syncAsByte(field_2);
+ s.syncAsByte(isSelectable);
+ s.syncAsByte(isPresent);
+ s.syncAsByte(manualSelect);
+ s.syncAsByte(location);
+ }
+ };
+
+ Inventory(LastExpressEngine *engine);
+ ~Inventory();
+
+ // Inventory contents
+ void addItem(InventoryItem item);
+ void removeItem(InventoryItem item, ObjectLocation newLocation = kObjectLocationNone);
+ bool hasItem(InventoryItem item);
+ void selectItem(InventoryItem item);
+ void unselectItem();
+ InventoryItem getSelectedItem() { return _selectedItem; }
+
+ InventoryEntry *get(InventoryItem item);
+ InventoryEntry *getSelectedEntry() { return get(_selectedItem); }
+
+ InventoryItem getFirstExaminableItem() const;
+ void setLocationAndProcess(InventoryItem item, ObjectLocation location);
+
+ // UI Control
+ void show();
+ void blinkEgg(bool enabled);
+ void showHourGlass() const;
+ void setPortrait(InventoryItem item) const;
+ void drawEgg() const;
+ void drawBlinkingEgg();
+
+ // Handle inventory UI events.
+ bool handleMouseEvent(const Common::Event &ev);
+
+ // State
+ bool isMagnifierInUse() { return _flagUseMagnifier; }
+ bool isFlag1() { return _flag1; }
+ bool isFlag2() { return _flag2; }
+ bool isEggHighlighted() { return _flagEggHightlighted; }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+ void saveSelectedItem(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ static const uint32 _defaultBlinkingInterval = 250; ///< Default blinking interval in ms
+
+ LastExpressEngine *_engine;
+
+ InventoryEntry _entries[32];
+ InventoryItem _selectedItem;
+ InventoryItem _highlightedItem;
+ bool _opened;
+ bool _visible;
+
+ bool _showingHourGlass;
+ bool _blinkingEgg;
+ uint32 _blinkingTime;
+ uint32 _blinkingInterval;
+ uint32 _blinkingBrightness;
+
+ // Flags
+ bool _flagUseMagnifier;
+ bool _flag1;
+ bool _flag2;
+ bool _flagEggHightlighted;
+
+ Scene *_itemScene;
+
+ // Important rects
+ Common::Rect _inventoryRect;
+ Common::Rect _menuRect;
+ Common::Rect _selectedRect;
+
+ void init();
+
+ void open();
+ void close();
+ void examine(InventoryItem item);
+ Common::Rect getItemRect(int16 index) const;
+
+ bool isItemSceneParameter(InventoryItem item) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_INVENTORY_H
diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp
new file mode 100644
index 0000000000..8e7f688235
--- /dev/null
+++ b/engines/lastexpress/game/logic.cpp
@@ -0,0 +1,593 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/logic.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+
+// Entities
+#include "lastexpress/entities/chapters.h"
+
+// Game
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+#define EVENT_TICKS_BEETWEEN_SAVEGAMES 450
+#define GAME_TICKS_BEETWEEN_SAVEGAMES 2700
+
+Logic::Logic(LastExpressEngine *engine) : _engine(engine) {
+ _action = new Action(engine);
+ _beetle = new Beetle(engine);
+ _entities = new Entities(engine);
+ _fight = new Fight(engine);
+ _saveload = new SaveLoad(engine);
+ _state = new State(engine);
+
+ // Flags
+ _flagActionPerformed = false;
+ _ignoreFrameInterval = false;
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+}
+
+Logic::~Logic() {
+ SAFE_DELETE(_action);
+ SAFE_DELETE(_beetle);
+ SAFE_DELETE(_fight);
+ SAFE_DELETE(_entities);
+ SAFE_DELETE(_saveload);
+ SAFE_DELETE(_state);
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Event Handling
+//////////////////////////////////////////////////////////////////////////
+#define REDRAW_CURSOR() { \
+ if (getInventory()->isMagnifierInUse()) \
+ _engine->getCursor()->setStyle(kCursorMagnifier); \
+ if (getInventory()->isFlag1() \
+ || getInventory()->isFlag2() \
+ || getInventory()->isEggHighlighted()) \
+ _engine->getCursor()->setStyle(kCursorNormal); \
+ return; \
+}
+
+void Logic::eventMouse(const Common::Event &ev) {
+ bool hotspotHandled = false;
+
+ // Reset mouse flags
+ getFlags()->mouseLeftClick = false;
+ getFlags()->mouseRightClick = false;
+
+ // Process event flags
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ if (getFlags()->frameInterval)
+ _ignoreFrameInterval = false;
+
+ getFlags()->frameInterval = false;
+ }
+
+ if (getFlags()->flag_0) {
+ if (ev.type == Common::EVENT_LBUTTONUP || ev.type == Common::EVENT_RBUTTONUP) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ getFlags()->frameInterval = true;
+ }
+ return;
+ }
+
+ if (_ignoreFrameInterval && getScenes()->checkCurrentPosition(true) && _engine->getCursor()->getStyle() == kCursorForward) {
+ getFlags()->shouldRedraw = false;
+ getFlags()->flag_0 = true;
+ return;
+ }
+
+ // Update coordinates
+ getGameState()->setCoordinates(ev.mouse);
+
+ // Handle inventory
+ getInventory()->handleMouseEvent(ev);
+
+ // Stop processing is inside the menu
+ if (getMenu()->isShown())
+ return;
+
+ // Handle whistle case
+ if (getInventory()->getSelectedItem() == kItemWhistle
+ && !getProgress().isEggOpen
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemWhistle)->cursor);
+
+ // Check if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP && !getSound()->isBuffered("LIB045")) {
+
+ getSound()->playSoundEvent(kEntityPlayer, 45);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 26) || getEntities()->isPlayerPosition(kCarGreenSleeping, 25) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction226078300);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 26) || getEntities()->isPlayerPosition(kCarRedSleeping, 25) || getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction226078300);
+ }
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle match case
+ if (getInventory()->getSelectedItem() == kItemMatch
+ && (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && getProgress().jacket == kJacketGreen
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()
+ && (getInventory()->get(kItem2)->location == kObjectLocationNone || getEntityData(kEntityPlayer)->car != kCarRedSleeping || getEntityData(kEntityPlayer)->entityPosition != kPosition_2300)) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemMatch)->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+
+ getScenes()->processScene();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle entity item case
+ EntityIndex entityIndex = getEntities()->canInteractWith(ev.mouse);
+ if (entityIndex
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ InventoryItem item = getEntityData(entityIndex)->inventoryItem;
+ if (getInventory()->hasItem((InventoryItem)(item & kItemToggleHigh))) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(item & kItemToggleHigh))->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, (InventoryItem)(item & kItemToggleHigh));
+ } else if ((InventoryItem)(item & kItemInvalid)) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(kCursorTalk2);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, kCursorNormal);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Handle standard actions
+ if (hotspotHandled || getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ return;
+
+ // Magnifier in use
+ if (getInventory()->isMagnifierInUse()) {
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted())
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ return;
+ }
+
+ // Check hotspots
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (!(*it)->isInside(ev.mouse))
+ continue;
+
+ if ((*it)->location < location)
+ continue;
+
+ if (!getAction()->getCursor(**it))
+ continue;
+
+ Scene *hotspotScene = getScenes()->get((*it)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*it)->cursor == kCursorTurnRight
+ || (*it)->cursor == kCursorTurnLeft) {
+ location = (*it)->location;
+ hotspot = *it;
+ }
+ }
+
+ // No hotspot found: show the normal cursor
+ if (!hotspot) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ return;
+ }
+
+ // Found an hotspot: update the cursor and perform the action if the user clicked the mouse
+ _engine->getCursor()->setStyle(getAction()->getCursor(*hotspot));
+
+ if (ev.type != Common::EVENT_LBUTTONUP || _flagActionPerformed)
+ return;
+
+ _flagActionPerformed = true;
+
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getFlags()->shouldRedraw = false;
+
+ getScenes()->setScene(testScene);
+
+ if (getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ // Switch to next chapter if necessary
+ if (hotspot->action == SceneHotspot::kActionSwitchChapter && hotspot->param1 == getState()->progress.chapter)
+ switchChapter();
+}
+
+void Logic::eventTick(const Common::Event &) {
+ uint ticks = 1;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust ticks if an action has been performed
+ if (_flagActionPerformed)
+ ticks = 10;
+
+ _flagActionPerformed = false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw the blinking egg if needed
+ if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawBlinkingEgg();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust time and save game if needed
+ if (getFlags()->isGameRunning) {
+ getState()->timeTicks += ticks;
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)(ticks * getState()->timeDelta));
+
+ if (getState()->timeDelta) {
+
+ // Auto-save
+ if (!_ticksSinceLastSavegame) {
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+ getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, kEventNone);
+ }
+
+ // Save after game ticks interval
+ if ((getState()->timeTicks - getSaveLoad()->getLastSavegameTicks()) > GAME_TICKS_BEETWEEN_SAVEGAMES)
+ getSaveLoad()->saveGame(kSavegameTypeTickInterval, kEntityChapters, kEventNone);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load scene and process hotspot
+ if (getFlags()->flag_0 && !getFlags()->mouseLeftClick && !getFlags()->mouseRightClick) {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ if (getScenes()->checkCurrentPosition(true)
+ && !getEntities()->getPosition(scene->car, scene->position)) {
+
+ // Process hotspot
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getScenes()->setScene(testScene);
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ if (getFlags()->isGameRunning)
+ getSavePoints()->callAndProcess();
+
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ return;
+ }
+
+ // Stop processing if the game is paused
+ if (!getFlags()->isGameRunning)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update beetle, savepoints, entities and draw frames
+ if (_beetle->isLoaded())
+ _beetle->update();
+
+ getSavePoints()->callAndProcess();
+ getEntities()->updateCallbacks();
+ getScenes()->drawFrames(true);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update cursor if we can interact with an entity
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (!entity) {
+ if (_engine->getCursor()->getStyle() >= kCursorTalk2)
+ updateCursor(false);
+
+ return;
+ }
+
+ // Show item cursor on entity
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh)) && (int)getEntityData(entity)->inventoryItem != (int)kCursorTalk2) {
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor);
+ return;
+ }
+
+ getLogic()->updateCursor(false);
+ _engine->getCursor()->setStyle(kCursorTalk2);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game over, Chapters & credits
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Resets the game state.
+ */
+void Logic::resetState() {
+ getState()->scene = kSceneDefault;
+
+ warning("Logic::resetState: not implemented! You need to restart the engine until this is implemented.");
+}
+
+/**
+ * Handle game over
+ *
+ * @param type The savegame type.
+ * @param value The value (event, time, index, ...)
+ * @param sceneIndex Index of the scene to show.
+ * @param showScene true to show a scene, false to return to menu directly
+ */
+void Logic::gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const {
+
+ getSound()->processEntries();
+ getEntities()->reset();
+ getFlags()->isGameRunning = false;
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ if (showScene) {
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ if (sceneIndex && !getFlags()->mouseRightClick) {
+ getScenes()->loadScene(sceneIndex);
+
+ while (getSound()->isBuffered(kEntityTables4)) {
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+
+ // Show Menu
+ getMenu()->show(false, type, value);
+}
+
+void Logic::switchChapter() const {
+ getSound()->clearStatus();
+
+ switch(getState()->progress.chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->addItem(kItemMatchBox);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter2);
+ break;
+
+ case kChapter2:
+ getInventory()->addItem(kItemScarf);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter3);
+ break;
+
+ case kChapter3:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation4;
+ getInventory()->get(kItemFirebird)->isPresent = false;
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getInventory()->addItem(kItemWhistle);
+ getInventory()->addItem(kItemKey);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter4);
+ break;
+
+ case kChapter4:
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter5);
+ break;
+
+ case kChapter5:
+ playFinalSequence();
+ break;
+ }
+}
+
+void Logic::playFinalSequence() const {
+ getSound()->processEntries();
+
+ _action->playAnimation(kEventFinalSequence);
+ showCredits();
+
+ getEntities()->reset();
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+}
+
+void Logic::showCredits() const {
+ error("Logic::showCredits: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void Logic::updateCursor(bool) const { /* the cursor is always updated, even when we don't want to redraw it */
+ CursorStyle style = kCursorNormal;
+ bool interact = false;
+
+ if (getInventory()->getSelectedItem() != kItemWhistle
+ || getProgress().isEggOpen
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()) {
+
+ if (getInventory()->getSelectedItem() != kItemMatch
+ || (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getProgress().jacket != kJacketGreen
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()
+ || (getInventory()->get(kItem2)->location
+ && getEntityData(kEntityPlayer)->car == kCarRedSleeping
+ && getEntityData(kEntityPlayer)->entityPosition == kPosition_2300)) {
+
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (entity
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))) {
+ interact = true;
+ style = getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor;
+ } else if ((int)getEntityData(entity)->inventoryItem == kItemInvalid) {
+ interact = true;
+ style = kCursorTalk2;
+ }
+ }
+
+ if (!interact
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ // Check all hotspots
+ for (Common::Array<SceneHotspot *>::iterator i = scene->getHotspots()->begin(); i != scene->getHotspots()->end(); ++i) {
+ if ((*i)->isInside(getCoords()) && (*i)->location >= location) {
+ if (getAction()->getCursor(**i)) {
+ Scene *hotspotScene = getScenes()->get((*i)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*i)->cursor == kCursorTurnRight
+ || (*i)->cursor == kCursorTurnLeft) {
+ hotspot = *i;
+ location = (*i)->location;
+ }
+ }
+ }
+ }
+
+ style = (hotspot) ? getAction()->getCursor(*hotspot) : kCursorNormal;
+ }
+ } else {
+ style = getInventory()->get(kItemMatch)->cursor;
+ }
+
+ } else {
+ style = getInventory()->get(kItemWhistle)->cursor;
+ }
+
+ if (getInventory()->isMagnifierInUse())
+ style = kCursorMagnifier;
+
+ if (getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ style = kCursorNormal;
+
+ _engine->getCursor()->setStyle(style);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h
new file mode 100644
index 0000000000..fc867d7680
--- /dev/null
+++ b/engines/lastexpress/game/logic.h
@@ -0,0 +1,92 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_LOGIC_H
+#define LASTEXPRESS_LOGIC_H
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/game/entities.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/events.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Action;
+class Beetle;
+class Debugger;
+class Entities;
+class Fight;
+class SaveLoad;
+class State;
+
+class Logic : public EventHandler {
+public:
+ Logic(LastExpressEngine *engine);
+ ~Logic();
+
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ void resetState();
+ void gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const;
+ void playFinalSequence() const;
+ void updateCursor(bool redraw = true) const;
+
+ Action *getGameAction() { return _action; }
+ Beetle *getGameBeetle() { return _beetle; }
+ Entities *getGameEntities() { return _entities; }
+ Fight *getGameFight() { return _fight; }
+ SaveLoad *getGameSaveLoad() { return _saveload; }
+ State *getGameState() { return _state; }
+
+private:
+ LastExpressEngine *_engine;
+
+ Action *_action; ///< Actions
+ Beetle *_beetle; ///< Beetle catching
+ Entities *_entities; ///< Entities
+ Fight *_fight; ///< Fight handling
+ SaveLoad *_saveload; ///< Save & loading
+ State *_state; ///< Game state
+
+ void switchChapter() const;
+ void showCredits() const;
+
+ // Flags & Members
+ bool _flagActionPerformed;
+ bool _ignoreFrameInterval;
+ int _ticksSinceLastSavegame;
+
+ friend class Debugger;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_LOGIC_H
diff --git a/engines/lastexpress/game/menu.cpp b/engines/lastexpress/game/menu.cpp
new file mode 100644
index 0000000000..9e38d05444
--- /dev/null
+++ b/engines/lastexpress/game/menu.cpp
@@ -0,0 +1,1544 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/menu.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/rational.h"
+
+#define getNextGameId() (GameId)((_gameId + 1) % 6)
+
+namespace LastExpress {
+
+// Bottom-left buttons (quit.seq)
+enum StartMenuButtons {
+ kButtonVolumeDownPushed,
+ kButtonVolumeDown,
+ kButtonVolume,
+ kButtonVolumeUp,
+ kButtonVolumeUpPushed,
+ kButtonBrightnessDownPushed, // 5
+ kButtonBrightnessDown,
+ kButtonBrightness,
+ kButtonBrightnessUp,
+ kButtonBrightnessUpPushed,
+ kButtonQuit, // 10
+ kButtonQuitPushed
+};
+
+// Egg buttons (buttns.seq)
+enum StartMenuEggButtons {
+ kButtonShield,
+ kButtonRewind,
+ kButtonRewindPushed,
+ kButtonForward,
+ kButtonForwardPushed,
+ kButtonCredits, // 5
+ kButtonCreditsPushed,
+ kButtonContinue
+};
+
+// Tooltips sequence (helpnewr.seq)
+enum StartMenuTooltips {
+ kTooltipInsertCd1,
+ kTooltipInsertCd2,
+ kTooltipInsertCd3,
+ kTooltipContinueGame,
+ kTooltipReplayGame,
+ kTooltipContinueRewoundGame, // 5
+ kTooltipViewGameEnding,
+ kTooltipStartAnotherGame,
+ kTooltipVolumeUp,
+ kTooltipVolumeDown,
+ kTooltipBrightnessUp, // 10
+ kTooltipBrightnessDown,
+ kTooltipQuit,
+ kTooltipRewindParis,
+ kTooltipForwardStrasbourg,
+ kTooltipRewindStrasbourg, // 15
+ kTooltipRewindMunich,
+ kTooltipForwardMunich,
+ kTooltipForwardVienna,
+ kTooltipRewindVienna,
+ kTooltipRewindBudapest, // 20
+ kTooltipForwardBudapest,
+ kTooltipForwardBelgrade,
+ kTooltipRewindBelgrade,
+ kTooltipForwardConstantinople,
+ kTooltipSwitchBlueGame, // 25
+ kTooltipSwitchRedGame,
+ kTooltipSwitchGoldGame,
+ kTooltipSwitchGreenGame,
+ kTooltipSwitchTealGame,
+ kTooltipSwitchPurpleGame, // 30
+ kTooltipPlayNewGame,
+ kTooltipCredits,
+ kTooltipFastForward,
+ kTooltipRewind
+};
+
+//////////////////////////////////////////////////////////////////////////
+// DATA
+//////////////////////////////////////////////////////////////////////////
+
+// Information about the cities on the train line
+static const struct {
+ uint8 frame;
+ TimeValue time;
+} _trainCities[31] = {
+ {0, kTimeCityParis},
+ {9, kTimeCityEpernay},
+ {11, kTimeCityChalons},
+ {16, kTimeCityBarLeDuc},
+ {21, kTimeCityNancy},
+ {25, kTimeCityLuneville},
+ {35, kTimeCityAvricourt},
+ {37, kTimeCityDeutschAvricourt},
+ {40, kTimeCityStrasbourg},
+ {53, kTimeCityBadenOos},
+ {56, kTimeCityKarlsruhe},
+ {60, kTimeCityStuttgart},
+ {63, kTimeCityGeislingen},
+ {66, kTimeCityUlm},
+ {68, kTimeCityAugsburg},
+ {73, kTimeCityMunich},
+ {84, kTimeCitySalzbourg},
+ {89, kTimeCityAttnangPuchheim},
+ {97, kTimeCityWels},
+ {100, kTimeCityLinz},
+ {104, kTimeCityAmstetten},
+ {111, kTimeCityVienna},
+ {120, kTimeCityPoszony},
+ {124, kTimeCityGalanta},
+ {132, kTimeCityBudapest},
+ {148, kTimeCityBelgrade},
+ /* Line 1 ends at 150 - line 2 begins at 0 */
+ {157, kTimeCityNish},
+ {165, kTimeCityTzaribrod},
+ {174, kTimeCitySofia},
+ {198, kTimeCityAdrianople},
+ {210, kTimeCityConstantinople}};
+
+static const struct {
+ TimeValue time;
+ uint index;
+ StartMenuTooltips rewind;
+ StartMenuTooltips forward;
+} _cityButtonsInfo[7] = {
+ {kTimeCityParis, 64, kTooltipRewindParis, kTooltipRewindParis},
+ {kTimeCityStrasbourg, 128, kTooltipRewindStrasbourg, kTooltipForwardStrasbourg},
+ {kTimeCityMunich, 129, kTooltipRewindMunich, kTooltipForwardMunich},
+ {kTimeCityVienna, 130, kTooltipRewindVienna, kTooltipForwardVienna},
+ {kTimeCityBudapest, 131, kTooltipRewindBudapest, kTooltipForwardBudapest},
+ {kTimeCityBelgrade, 132, kTooltipRewindBelgrade, kTooltipForwardBelgrade},
+ {kTimeCityConstantinople, 192, kTooltipForwardConstantinople, kTooltipForwardConstantinople}
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Clock
+//////////////////////////////////////////////////////////////////////////
+class Clock {
+public:
+ Clock(LastExpressEngine *engine);
+ ~Clock();
+
+ void draw(uint32 time);
+ void clear();
+
+private:
+ LastExpressEngine *_engine;
+
+ // Frames
+ SequenceFrame *_frameMinutes;
+ SequenceFrame *_frameHour;
+ SequenceFrame *_frameSun;
+ SequenceFrame *_frameDate;
+};
+
+Clock::Clock(LastExpressEngine *engine) : _engine(engine), _frameMinutes(NULL), _frameHour(NULL), _frameSun(NULL), _frameDate(NULL) {
+ _frameMinutes = new SequenceFrame(loadSequence("eggmin.seq"), 0, true);
+ _frameHour = new SequenceFrame(loadSequence("egghour.seq"), 0, true);
+ _frameSun = new SequenceFrame(loadSequence("sun.seq"), 0, true);
+ _frameDate = new SequenceFrame(loadSequence("datenew.seq"), 0, true);
+}
+
+Clock::~Clock() {
+ SAFE_DELETE(_frameMinutes);
+ SAFE_DELETE(_frameHour);
+ SAFE_DELETE(_frameSun);
+ SAFE_DELETE(_frameDate);
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+void Clock::clear() {
+ getScenes()->removeFromQueue(_frameMinutes);
+ getScenes()->removeFromQueue(_frameHour);
+ getScenes()->removeFromQueue(_frameSun);
+ getScenes()->removeFromQueue(_frameDate);
+}
+
+void Clock::draw(uint32 time) {
+ assert(time >= kTimeCityParis && time <= kTimeCityConstantinople);
+
+ // Check that sequences have been loaded
+ if (!_frameMinutes || !_frameHour || !_frameSun || !_frameDate)
+ error("Clock::process: clock sequences have not been loaded correctly!");
+
+ // Clear existing frames
+ clear();
+
+ // Game starts at: 1037700 = 7:13 p.m. on July 24, 1914
+ // Game ends at: 4941000 = 7:30 p.m. on July 26, 1914
+ // Game lasts for: 3903300 = 2 days + 17 mins = 2897 mins
+
+ // 15 = 1 second
+ // 15 * 60 = 900 = 1 minute
+ // 900 * 60 = 54000 = 1 hour
+ // 54000 * 24 = 1296000 = 1 day
+
+ // Calculate each sequence index from the current time
+
+ uint8 hour = 0;
+ uint8 minute = 0;
+ State::getHourMinutes(time, &hour, &minute);
+ uint32 index_date = 18 * time / 1296000;
+ if (hour == 23)
+ index_date += 18 * minute / 60;
+
+ // Set sequences frames
+ _frameMinutes->setFrame(minute);
+ _frameHour->setFrame((5 * hour + minute / 12) % 60);
+ _frameSun->setFrame((5 * hour + minute / 12) % 120);
+ _frameDate->setFrame((uint16)index_date);
+
+ // Adjust z-order and queue
+ _frameMinutes->getInfo()->location = 1;
+ _frameHour->getInfo()->location = 1;
+ _frameSun->getInfo()->location = 1;
+ _frameDate->getInfo()->location = 1;
+
+ getScenes()->addToQueue(_frameMinutes);
+ getScenes()->addToQueue(_frameHour);
+ getScenes()->addToQueue(_frameSun);
+ getScenes()->addToQueue(_frameDate);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// TrainLine
+//////////////////////////////////////////////////////////////////////////
+class TrainLine {
+public:
+ TrainLine(LastExpressEngine *engine);
+ ~TrainLine();
+
+ void draw(uint32 time);
+ void clear();
+
+private:
+ LastExpressEngine *_engine;
+
+ // Frames
+ SequenceFrame *_frameLine1;
+ SequenceFrame *_frameLine2;
+};
+
+TrainLine::TrainLine(LastExpressEngine *engine) : _engine(engine), _frameLine1(NULL), _frameLine2(NULL) {
+ _frameLine1 = new SequenceFrame(loadSequence("line1.seq"), 0, true);
+ _frameLine2 = new SequenceFrame(loadSequence("line2.seq"), 0, true);
+}
+
+TrainLine::~TrainLine() {
+ SAFE_DELETE(_frameLine1);
+ SAFE_DELETE(_frameLine2);
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+void TrainLine::clear() {
+ getScenes()->removeFromQueue(_frameLine1);
+ getScenes()->removeFromQueue(_frameLine2);
+}
+
+// Draw the train line at the time
+// line1: 150 frames (=> Belgrade)
+// line2: 61 frames (=> Constantinople)
+void TrainLine::draw(uint32 time) {
+ assert(time >= kTimeCityParis && time <= kTimeCityConstantinople);
+
+ // Check that sequences have been loaded
+ if (!_frameLine1 || !_frameLine2)
+ error("TrainLine::process: Line sequences have not been loaded correctly!");
+
+ // Clear existing frames
+ clear();
+
+ // Get the index of the last city the train has visited
+ uint index = 0;
+ for (uint i = 0; i < ARRAYSIZE(_trainCities); i++)
+ if ((uint32)_trainCities[i].time <= time)
+ index = i;
+
+ uint16 frame;
+ if (time > (uint32)_trainCities[index].time) {
+ // Interpolate linearly to use a frame between the cities
+ uint8 diffFrames = _trainCities[index + 1].frame - _trainCities[index].frame;
+ uint diffTimeCities = (uint)(_trainCities[index + 1].time - _trainCities[index].time);
+ uint traveledTime = (time - (uint)_trainCities[index].time);
+ frame = (uint16)(_trainCities[index].frame + (traveledTime * diffFrames) / diffTimeCities);
+ } else {
+ // Exactly on the city
+ frame = _trainCities[index].frame;
+ }
+
+ // Set frame, z-order and queue
+ if (frame < 150) {
+ _frameLine1->setFrame(frame);
+
+ _frameLine1->getInfo()->location = 1;
+ getScenes()->addToQueue(_frameLine1);
+ } else {
+ // We passed Belgrade
+ _frameLine1->setFrame(149);
+ _frameLine2->setFrame(frame - 150);
+
+ _frameLine1->getInfo()->location = 1;
+ _frameLine2->getInfo()->location = 1;
+
+ getScenes()->addToQueue(_frameLine1);
+ getScenes()->addToQueue(_frameLine2);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Menu
+//////////////////////////////////////////////////////////////////////////
+Menu::Menu(LastExpressEngine *engine) : _engine(engine),
+ _seqTooltips(NULL), _seqEggButtons(NULL), _seqButtons(NULL), _seqAcorn(NULL), _seqCity1(NULL), _seqCity2(NULL), _seqCity3(NULL), _seqCredits(NULL),
+ _gameId(kGameBlue), _hasShownStartScreen(false), _hasShownIntro(false),
+ _isShowingCredits(false), _isGameStarted(false), _isShowingMenu(false),
+ _creditsSequenceIndex(0), _checkHotspotsTicks(15), _mouseFlags(Common::EVENT_INVALID), _lastHotspot(NULL),
+ _currentTime(kTimeNone), _lowerTime(kTimeNone), _time(kTimeNone), _currentIndex(0), _index(0), _lastIndex(0), _delta(0), _handleTimeDelta(false) {
+
+ _clock = new Clock(_engine);
+ _trainLine = new TrainLine(_engine);
+}
+
+Menu::~Menu() {
+ SAFE_DELETE(_clock);
+ SAFE_DELETE(_trainLine);
+
+ SAFE_DELETE(_seqTooltips);
+ SAFE_DELETE(_seqEggButtons);
+ SAFE_DELETE(_seqButtons);
+ SAFE_DELETE(_seqAcorn);
+ SAFE_DELETE(_seqCity1);
+ SAFE_DELETE(_seqCity2);
+ SAFE_DELETE(_seqCity3);
+ SAFE_DELETE(_seqCredits);
+
+ _lastHotspot = NULL;
+
+ // Cleanup frames
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ SAFE_DELETE(it->_value);
+
+ _frames.clear();
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+void Menu::setup() {
+
+ // Clear drawing queue
+ getScenes()->removeAndRedraw(&_frames[kOverlayAcorn], false);
+ SAFE_DELETE(_seqAcorn);
+
+ // Load Menu scene
+ // + 1 = normal menu with open egg / clock
+ // + 2 = shield menu, when no savegame exists (no game has been started)
+ _isGameStarted = _lowerTime >= kTimeStartGame;
+ getScenes()->loadScene((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2));
+ getFlags()->shouldRedraw = true;
+ getLogic()->updateCursor();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load Acorn sequence
+ _seqAcorn = loadSequence(getAcornSequenceName(_isGameStarted ? getNextGameId() : kGameBlue));
+
+ //////////////////////////////////////////////////////////////////////////
+ // Check if we loaded sequences before
+ if (_seqTooltips && _seqTooltips->count() > 0)
+ return;
+
+ // Load all static data
+ _seqTooltips = loadSequence("helpnewr.seq");
+ _seqEggButtons = loadSequence("buttns.seq");
+ _seqButtons = loadSequence("quit.seq");
+ _seqCity1 = loadSequence("jlinetl.seq");
+ _seqCity2 = loadSequence("jlinecen.seq");
+ _seqCity3 = loadSequence("jlinebr.seq");
+ _seqCredits = loadSequence("credits.seq");
+
+ _frames[kOverlayTooltip] = new SequenceFrame(_seqTooltips);
+ _frames[kOverlayEggButtons] = new SequenceFrame(_seqEggButtons);
+ _frames[kOverlayButtons] = new SequenceFrame(_seqButtons);
+ _frames[kOverlayAcorn] = new SequenceFrame(_seqAcorn);
+ _frames[kOverlayCity1] = new SequenceFrame(_seqCity1);
+ _frames[kOverlayCity2] = new SequenceFrame(_seqCity2);
+ _frames[kOverlayCity3] = new SequenceFrame(_seqCity3);
+ _frames[kOverlayCredits] = new SequenceFrame(_seqCredits);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Handle events
+void Menu::eventMouse(const Common::Event &ev) {
+ if (!getFlags()->shouldRedraw)
+ return;
+
+ bool redraw = true;
+ getFlags()->shouldRedraw = false;
+
+ // Update coordinates
+ setCoords(ev.mouse);
+ //_mouseFlags = (Common::EventType)(ev.type & Common::EVENT_LBUTTONUP);
+
+ if (_isShowingCredits) {
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ showFrame(kOverlayCredits, -1, true);
+ _isShowingCredits = false;
+ }
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ // Last frame of the credits
+ if (_seqCredits && _creditsSequenceIndex == _seqCredits->count() - 1) {
+ showFrame(kOverlayCredits, -1, true);
+ _isShowingCredits = false;
+ } else {
+ ++_creditsSequenceIndex;
+ showFrame(kOverlayCredits, _creditsSequenceIndex, true);
+ }
+ }
+ } else {
+ // Check for hotspots
+ SceneHotspot *hotspot = NULL;
+ getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot);
+
+ if (_lastHotspot != hotspot || ev.type == Common::EVENT_LBUTTONUP) {
+ _lastHotspot = hotspot;
+
+ if (ev.type == Common::EVENT_MOUSEMOVE) { /* todo check event type */
+ if (!_handleTimeDelta && hasTimeDelta())
+ setTime();
+ }
+
+ if (hotspot) {
+ redraw = handleEvent((StartMenuAction)hotspot->action, ev.type);
+ getFlags()->mouseRightClick = false;
+ getFlags()->mouseLeftClick = false;
+ } else {
+ hideOverlays();
+ }
+ }
+ }
+
+ if (redraw) {
+ getFlags()->shouldRedraw = true;
+ askForRedraw();
+ }
+}
+
+void Menu::eventTick(const Common::Event&) {
+ if (hasTimeDelta())
+ adjustTime();
+ else if (_handleTimeDelta)
+ _handleTimeDelta = false;
+
+ // Check hotspots
+ if (!--_checkHotspotsTicks) {
+ checkHotspots();
+ _checkHotspotsTicks = 15;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Show the intro and load the main menu scene
+void Menu::show(bool doSavegame, SavegameType type, uint32 value) {
+
+ if (_isShowingMenu)
+ return;
+
+ _isShowingMenu = true;
+ getEntities()->reset();
+
+ // If no blue savegame exists, this might be the first time we start the game, so we show the full intro
+ if (!getFlags()->mouseRightClick) {
+ if (!SaveLoad::isSavegameValid(kGameBlue) && _engine->getResourceManager()->loadArchive(kArchiveCd1)) {
+
+ if (!_hasShownIntro) {
+ // Show Broderbrund logo
+ Animation animation;
+ if (animation.load(getArchive("1930.nis")))
+ animation.play();
+
+ getFlags()->mouseRightClick = false;
+
+ // Play intro music
+ getSound()->playSoundWithSubtitles("MUS001.SND", SoundManager::kFlagMusic, kEntityPlayer);
+
+ // Show The Smoking Car logo
+ if (animation.load(getArchive("1931.nis")))
+ animation.play();
+
+ _hasShownIntro = true;
+ }
+ } else {
+ // Only show the quick intro
+ if (!_hasShownStartScreen) {
+ getSound()->playSoundWithSubtitles("MUS018.SND", SoundManager::kFlagMusic, kEntityPlayer);
+ getScenes()->loadScene(kSceneStartScreen);
+
+ // Original game waits 60 frames and loops Sound::unknownFunction1 unless the right button is pressed
+ uint32 nextFrameCount = getFrameCount() + 60;
+ while (getFrameCount() < nextFrameCount) {
+ _engine->pollEvents();
+
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+ }
+
+ _hasShownStartScreen = true;
+
+ // Init Menu
+ init(doSavegame, type, value);
+
+ // Setup sound
+ getSound()->unknownFunction4();
+ getSound()->resetQueue(SoundManager::kSoundType11, SoundManager::kSoundType13);
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+
+ // Init flags & misc
+ _isShowingCredits = false;
+ _handleTimeDelta = hasTimeDelta();
+ getInventory()->unselectItem();
+
+ // Set Cursor type
+ _engine->getCursor()->setStyle(kCursorNormal);
+ _engine->getCursor()->show(true);
+
+ setup();
+ checkHotspots();
+
+ // Set event handlers
+ SET_EVENT_HANDLERS(Menu, this);
+}
+
+bool Menu::handleEvent(StartMenuAction action, Common::EventType type) {
+ bool clicked = (type == Common::EVENT_LBUTTONUP);
+
+ switch(action) {
+ default:
+ hideOverlays();
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuCredits:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ showFrame(kOverlayEggButtons, kButtonCreditsPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ hideOverlays();
+
+ _isShowingCredits = true;
+ _creditsSequenceIndex = 0;
+
+ showFrame(kOverlayCredits, 0, true);
+ } else {
+ // TODO check flags ?
+
+ showFrame(kOverlayEggButtons, kButtonCredits, true);
+ showFrame(kOverlayTooltip, kTooltipCredits, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuQuitGame:
+ showFrame(kOverlayTooltip, kTooltipQuit, true);
+
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonQuitPushed, true);
+
+ getSound()->clearStatus();
+ getSound()->updateQueue();
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ // FIXME uncomment when sound queue is properly implemented
+ /*while (getSound()->isBuffered("LIB046"))
+ getSound()->updateQueue();*/
+
+ getFlags()->shouldRedraw = false;
+
+ Engine::quitGame();
+
+ return false;
+ } else {
+ showFrame(kOverlayButtons, kButtonQuit, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuCase4:
+ if (clicked)
+ _index = 0;
+ // fall down to kMenuContinue
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuContinue: {
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Determine the proper CD archive
+ ArchiveIndex cd = kArchiveCd1;
+ if (getProgress().chapter > kChapter1)
+ cd = (getProgress().chapter > kChapter3) ? kArchiveCd3 : kArchiveCd2;
+
+ // Show tooltips & buttons to start a game, continue a game or load the proper cd
+ if (ResourceManager::isArchivePresent(cd)) {
+ if (_isGameStarted) {
+ showFrame(kOverlayEggButtons, kButtonContinue, true);
+
+ if (_lastIndex == _index) {
+ showFrame(kOverlayTooltip, getSaveLoad()->isGameFinished(_index, _lastIndex) ? kTooltipViewGameEnding : kTooltipContinueGame, true);
+ } else {
+ showFrame(kOverlayTooltip, kTooltipContinueRewoundGame, true);
+ }
+
+ } else {
+ showFrame(kOverlayEggButtons, kButtonShield, true);
+ showFrame(kOverlayTooltip, kTooltipPlayNewGame, true);
+ }
+ } else {
+ showFrame(kOverlayEggButtons, -1, true);
+ showFrame(kOverlayTooltip, cd - 1, true);
+ }
+
+ if (!clicked)
+ break;
+
+ // Try loading the archive file
+ if (!_engine->getResourceManager()->loadArchive(cd))
+ break;
+
+ // Load the train data file and setup game
+ getScenes()->loadSceneDataFile(cd);
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ // Setup new game
+ getSavePoints()->reset();
+ setLogicEventHandlers();
+
+ if (_index) {
+ getSound()->processEntry(SoundManager::kSoundType11);
+ } else {
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 3));
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 4));
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 5));
+
+ if (!getFlags()->mouseRightClick) {
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ // Show intro
+ Animation animation;
+ if (animation.load(getArchive("1601.nis")))
+ animation.play();
+
+ getEvent(kEventIntro) = 1;
+ }
+ }
+ }
+ }
+
+ if (!getEvent(kEventIntro)) {
+ getEvent(kEventIntro) = 1;
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+ }
+ }
+
+ // Setup game
+ getFlags()->isGameRunning = true;
+ startGame();
+
+ if (!_isShowingMenu)
+ getInventory()->show();
+
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuSwitchSaveGame:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ showFrame(kOverlayAcorn, 1, true);
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB047");
+
+ // Setup new menu screen
+ switchGame();
+ setup();
+
+ // Set fight state to 0
+ getFight()->resetState();
+
+ return true;
+ }
+
+ // TODO Check for flag
+
+ showFrame(kOverlayAcorn, 0, true);
+
+ if (_isGameStarted) {
+ showFrame(kOverlayTooltip, kTooltipSwitchBlueGame, true);
+ break;
+ }
+
+ if (_gameId == kGameGold) {
+ showFrame(kOverlayTooltip, kTooltipSwitchBlueGame, true);
+ break;
+ }
+
+ if (!SaveLoad::isSavegameValid(getNextGameId())) {
+ showFrame(kOverlayTooltip, kTooltipStartAnotherGame, true);
+ break;
+ }
+
+ // Stupid tooltips ids are not in order, so we can't just increment them...
+ switch(_gameId) {
+ default:
+ break;
+
+ case kGameBlue:
+ showFrame(kOverlayTooltip, kTooltipSwitchRedGame, true);
+ break;
+
+ case kGameRed:
+ showFrame(kOverlayTooltip, kTooltipSwitchGreenGame, true);
+ break;
+
+ case kGameGreen:
+ showFrame(kOverlayTooltip, kTooltipSwitchPurpleGame, true);
+ break;
+
+ case kGamePurple:
+ showFrame(kOverlayTooltip, kTooltipSwitchTealGame, true);
+ break;
+
+ case kGameTeal:
+ showFrame(kOverlayTooltip, kTooltipSwitchGoldGame, true);
+ break;
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuRewindGame:
+ if (!_index || _currentTime < _time) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ if (hasTimeDelta())
+ _handleTimeDelta = false;
+
+ showFrame(kOverlayEggButtons, kButtonRewindPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ rewindTime();
+
+ _handleTimeDelta = false;
+ } else {
+ showFrame(kOverlayEggButtons, kButtonRewind, true);
+ showFrame(kOverlayTooltip, kTooltipRewind, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuForwardGame:
+ if (_lastIndex <= _index || _currentTime > _time) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ if (hasTimeDelta())
+ _handleTimeDelta = false;
+
+ showFrame(kOverlayEggButtons, kButtonForwardPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ forwardTime();
+
+ _handleTimeDelta = false;
+ } else {
+ showFrame(kOverlayEggButtons, kButtonForward, true);
+ showFrame(kOverlayTooltip, kTooltipFastForward, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuParis:
+ moveToCity(kParis, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuStrasBourg:
+ moveToCity(kStrasbourg, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuMunich:
+ moveToCity(kMunich, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuVienna:
+ moveToCity(kVienna, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuBudapest:
+ moveToCity(kBudapest, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuBelgrade:
+ moveToCity(kBelgrade, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuConstantinople:
+ moveToCity(kConstantinople, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuDecreaseVolume:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot decrease volume further
+ if (getVolume() == 0) {
+ showFrame(kOverlayButtons, kButtonVolume, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipVolumeDown, true);
+
+ // Show highlight on button & adjust volume if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonVolumeDownPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setVolume(getVolume() - 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ uint32 nextFrameCount = getFrameCount() + 15;
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ getSound()->updateQueue();
+ }
+ } else {
+ showFrame(kOverlayButtons, kButtonVolumeDown, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuIncreaseVolume:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase volume further
+ if (getVolume() >= 7) {
+ showFrame(kOverlayButtons, kButtonVolume, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipVolumeUp, true);
+
+ // Show highlight on button & adjust volume if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonVolumeUpPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setVolume(getVolume() + 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ uint32 nextFrameCount = getFrameCount() + 15;
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ getSound()->updateQueue();
+ }
+ } else {
+ showFrame(kOverlayButtons, kButtonVolumeUp, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuDecreaseBrightness:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase brightness further
+ if (getBrightness() == 0) {
+ showFrame(kOverlayButtons, kButtonBrightness, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipBrightnessDown, true);
+
+ // Show highlight on button & adjust brightness if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonBrightnessDownPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setBrightness(getBrightness() - 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ // Reshow the background and frames (they will pick up the new brightness through the GraphicsManager)
+ _engine->getGraphicsManager()->draw(getScenes()->get((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2)), GraphicsManager::kBackgroundC, true);
+ showFrame(kOverlayTooltip, kTooltipBrightnessDown, false);
+ showFrame(kOverlayButtons, kButtonBrightnessDownPushed, false);
+ } else {
+ showFrame(kOverlayButtons, kButtonBrightnessDown, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuIncreaseBrightness:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase brightness further
+ if (getBrightness() >= 6) {
+ showFrame(kOverlayButtons, kButtonBrightness, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipBrightnessUp, true);
+
+ // Show highlight on button & adjust brightness if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonBrightnessUpPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setBrightness(getBrightness() + 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ // Reshow the background and frames (they will pick up the new brightness through the GraphicsManager)
+ _engine->getGraphicsManager()->draw(getScenes()->get((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2)), GraphicsManager::kBackgroundC, true);
+ showFrame(kOverlayTooltip, kTooltipBrightnessUp, false);
+ showFrame(kOverlayButtons, kButtonBrightnessUpPushed, false);
+ } else {
+ showFrame(kOverlayButtons, kButtonBrightnessUp, true);
+ }
+ break;
+ }
+
+ return true;
+}
+
+void Menu::setLogicEventHandlers() {
+ SET_EVENT_HANDLERS(Logic, getLogic());
+ clear();
+ _isShowingMenu = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game-related
+//////////////////////////////////////////////////////////////////////////
+void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
+
+ bool useSameIndex = true;
+
+ if (getGlobalTimer()) {
+ value = 0;
+
+ // Check if the CD file is present
+ ArchiveIndex index = kArchiveCd1;
+ switch (getProgress().chapter) {
+ default:
+ case kChapter1:
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ index = kArchiveCd2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ index = kArchiveCd3;
+ break;
+ }
+
+ if (ResourceManager::isArchivePresent(index)) {
+ setGlobalTimer(0);
+ useSameIndex = false;
+
+ // TODO remove existing savegame and reset index & savegame name
+ warning("Menu::initGame: not implemented!");
+ }
+
+ doSavegame = false;
+ } else {
+ // TODO rename saves?
+ }
+
+ // Create a new savegame if needed
+ if (!SaveLoad::isSavegamePresent(_gameId))
+ getSaveLoad()->create(_gameId);
+
+ if (doSavegame)
+ getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone);
+
+ if (!getGlobalTimer()) {
+ // TODO: remove existing savegame temp file
+ }
+
+ // Init savegame & menu values
+ _lastIndex = getSaveLoad()->init(_gameId, true);
+ _lowerTime = getSaveLoad()->getTime(_lastIndex);
+
+ if (useSameIndex)
+ _index = _lastIndex;
+
+ //if (!getGlobalTimer())
+ // _index3 = 0;
+
+ if (!getProgress().chapter)
+ getProgress().chapter = kChapter1;
+
+ getState()->time = (TimeValue)getSaveLoad()->getTime(_index);
+ getProgress().chapter = getSaveLoad()->getChapter(_index);
+
+ if (_lowerTime >= kTimeStartGame) {
+ _currentTime = (uint32)getState()->time;
+ _time = (uint32)getState()->time;
+ _clock->draw(_time);
+ _trainLine->draw(_time);
+
+ initTime(type, value);
+ }
+}
+
+// Start a game (or load an existing savegame)
+void Menu::startGame() {
+ // Clear savegame headers
+ getSaveLoad()->clear();
+
+ // Hide menu elements
+ _clock->clear();
+ _trainLine->clear();
+
+ if (_lastIndex == _index) {
+ setGlobalTimer(0);
+ if (_index) {
+ getSaveLoad()->loadGame(_gameId);
+ } else {
+ getLogic()->resetState();
+ getEntities()->setup(true, kEntityPlayer);
+ }
+ } else {
+ getSaveLoad()->loadGame(_gameId, _index);
+ }
+}
+
+// Switch to the next savegame
+void Menu::switchGame() {
+
+ // Switch back to blue game is the current game is not started
+ _gameId = SaveLoad::isSavegameValid(_gameId) ? getNextGameId() : kGameBlue;
+
+ // Initialize savegame if needed
+ if (!SaveLoad::isSavegamePresent(_gameId))
+ getSaveLoad()->create(_gameId);
+
+ getState()->time = kTimeNone;
+
+ // Clear menu elements
+ _clock->clear();
+ _trainLine->clear();
+
+ // Clear loaded savegame data
+ getSaveLoad()->clear(true);
+
+ init(false, kSavegameTypeIndex, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Overlays & elements
+//////////////////////////////////////////////////////////////////////////
+void Menu::checkHotspots() {
+ if (!_isShowingMenu)
+ return;
+
+ if (!getFlags()->shouldRedraw)
+ return;
+
+ if (_isShowingCredits)
+ return;
+
+ SceneHotspot *hotspot = NULL;
+ getScenes()->get(getState()->scene)->checkHotSpot(getCoords(), &hotspot);
+
+ if (hotspot)
+ handleEvent((StartMenuAction)hotspot->action, _mouseFlags);
+ else
+ hideOverlays();
+}
+
+void Menu::hideOverlays() {
+ _lastHotspot = NULL;
+
+ // Hide all menu overlays
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ showFrame(it->_key, -1, false);
+
+ getScenes()->drawFrames(true);
+}
+
+void Menu::showFrame(StartMenuOverlay overlayType, int index, bool redraw) {
+ if (index == -1) {
+ getScenes()->removeFromQueue(_frames[overlayType]);
+ } else {
+ // Check that the overlay is valid
+ if (!_frames[overlayType])
+ return;
+
+ // Remove the frame and add a new one with the proper index
+ getScenes()->removeFromQueue(_frames[overlayType]);
+ _frames[overlayType]->setFrame((uint16)index);
+ getScenes()->addToQueue(_frames[overlayType]);
+ }
+
+ if (redraw)
+ getScenes()->drawFrames(true);
+}
+
+// Remove all frames from the queue
+void Menu::clear() {
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ getScenes()->removeAndRedraw(&it->_value, false);
+
+ clearBg(GraphicsManager::kBackgroundOverlay);
+}
+
+// Get the sequence name to use for the acorn highlight, depending of the currently loaded savegame
+Common::String Menu::getAcornSequenceName(GameId id) const {
+ Common::String name = "";
+ switch (id) {
+ default:
+ case kGameBlue:
+ name = "aconblu3.seq";
+ break;
+
+ case kGameRed:
+ name = "aconred.seq";
+ break;
+
+ case kGameGreen:
+ name = "acongren.seq";
+ break;
+
+ case kGamePurple:
+ name = "aconpurp.seq";
+ break;
+
+ case kGameTeal:
+ name = "aconteal.seq";
+ break;
+
+ case kGameGold:
+ name = "acongold.seq";
+ break;
+ }
+
+ return name;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Time
+//////////////////////////////////////////////////////////////////////////
+void Menu::initTime(SavegameType type, uint32 value) {
+ if (!value)
+ return;
+
+ // The savegame entry index
+ uint32 entryIndex = 0;
+
+ switch (type) {
+ default:
+ break;
+
+ case kSavegameTypeIndex:
+ entryIndex = (_index <= value) ? 1 : _index - value;
+ break;
+
+ case kSavegameTypeTime:
+ if (value < kTimeStartGame)
+ break;
+
+ entryIndex = _index;
+ if (!entryIndex)
+ break;
+
+ // Iterate through existing entries
+ do {
+ if (getSaveLoad()->getTime(entryIndex) <= value)
+ break;
+
+ entryIndex--;
+ } while (entryIndex);
+ break;
+
+ case kSavegameTypeEvent:
+ entryIndex = _index;
+ if (!entryIndex)
+ break;
+
+ do {
+ if (getSaveLoad()->getValue(entryIndex) == value)
+ break;
+
+ entryIndex--;
+ } while (entryIndex);
+ break;
+
+ case kSavegameTypeEvent2:
+ // TODO rewrite in a more legible way
+ if (_index > 1) {
+ uint32 index = _index;
+ do {
+ if (getSaveLoad()->getValue(index) == value)
+ break;
+
+ index--;
+ } while (index > 1);
+
+ entryIndex = index - 1;
+ } else {
+ entryIndex = _index - 1;
+ }
+ break;
+ }
+
+ if (entryIndex) {
+ _currentIndex = entryIndex;
+ updateTime(getSaveLoad()->getTime(entryIndex));
+ }
+}
+
+void Menu::updateTime(uint32 time) {
+ if (_currentTime == _time)
+ _delta = 0;
+
+ _currentTime = time;
+
+ if (_time != time) {
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getSound()->playSoundWithSubtitles((_currentTime >= _time) ? "LIB042" : "LIB041", SoundManager::kFlagMenuClock, kEntityChapters);
+ adjustIndex(_currentTime, _time, false);
+ }
+}
+
+void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
+ uint32 index = 0;
+ int32 timeDelta = -1;
+
+ if (time1 != time2) {
+
+ index = _index;
+
+ if (time2 >= time1) {
+ if (searchEntry) {
+ uint32 currentIndex = _index;
+
+ if ((int32)_index >= 0) {
+ do {
+ // Calculate new delta
+ int32 newDelta = time1 - (uint32)getSaveLoad()->getTime(currentIndex);
+
+ if (newDelta >= 0 && timeDelta >= newDelta) {
+ timeDelta = newDelta;
+ index = currentIndex;
+ }
+
+ --currentIndex;
+ } while ((int32)currentIndex >= 0);
+ }
+ } else {
+ index = _index - 1;
+ }
+ } else {
+ if (searchEntry) {
+ uint32 currentIndex = _index;
+
+ if (_lastIndex >= _index) {
+ do {
+ // Calculate new delta
+ int32 newDelta = (uint32)getSaveLoad()->getTime(currentIndex) - time1;
+
+ if (newDelta >= 0 && timeDelta > newDelta) {
+ timeDelta = newDelta;
+ index = currentIndex;
+ }
+
+ ++currentIndex;
+ } while (currentIndex <= _lastIndex);
+ }
+ } else {
+ index = _index + 1;
+ }
+ }
+
+ _index = index;
+ checkHotspots();
+ }
+
+ if (_index == _currentIndex) {
+ if (getProgress().chapter != getSaveLoad()->getChapter(index))
+ getProgress().chapter = getSaveLoad()->getChapter(_index);
+ }
+}
+
+void Menu::goToTime(uint32 time) {
+
+ uint32 entryIndex = 0;
+ uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getTime(0) - time));
+ uint32 index = 0;
+
+ do {
+ uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getTime(index) - time));
+ if (deltaTime2 < deltaTime) {
+ deltaTime = deltaTime2;
+ entryIndex = index;
+ }
+
+ ++index;
+ } while (_lastIndex >= index);
+
+ _currentIndex = entryIndex;
+ updateTime(getSaveLoad()->getTime(entryIndex));
+}
+
+void Menu::setTime() {
+ _currentIndex = _index;
+ _currentTime = getSaveLoad()->getTime(_currentIndex);
+
+ if (_time == _currentTime)
+ adjustTime();
+}
+
+void Menu::forwardTime() {
+ if (_lastIndex <= _index)
+ return;
+
+ _currentIndex = _lastIndex;
+ updateTime(getSaveLoad()->getTime(_currentIndex));
+}
+
+void Menu::rewindTime() {
+ if (!_index)
+ return;
+
+ _currentIndex = 0;
+ updateTime(getSaveLoad()->getTime(_currentIndex));
+}
+
+void Menu::adjustTime() {
+ uint32 originalTime = _time;
+
+ // Adjust time delta
+ Common::Rational timeDelta(_delta >= 90 ? 9 : (9 * _delta + 89), _delta >= 90 ? 1 : 90);
+
+ if (_currentTime < _time) {
+ timeDelta *= 900;
+ _time -= timeDelta.toInt();
+
+ if (_currentTime > _time)
+ _time = _currentTime;
+ } else {
+ timeDelta *= 900;
+ _time += timeDelta.toInt();
+
+ if (_currentTime < _time)
+ _time = _currentTime;
+ }
+
+ if (_currentTime == _time && getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ _clock->draw(_time);
+ _trainLine->draw(_time);
+ getScenes()->drawFrames(true);
+
+ adjustIndex(_time, originalTime, true);
+
+ ++_delta;
+}
+
+void Menu::moveToCity(CityButton city, bool clicked) {
+ uint32 time = (uint32)_cityButtonsInfo[city].time;
+
+ // TODO Check if we have access (there seems to be more checks on some internal times) - probably : current_time (menu only) / game time / some other?
+ if (_lowerTime < time || _time == time || _currentTime == time) {
+ hideOverlays();
+ return;
+ }
+
+ // Show city overlay
+ showFrame((StartMenuOverlay)((_cityButtonsInfo[city].index >> 6) + 3), _cityButtonsInfo[city].index & 63, true);
+
+ if (clicked) {
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ goToTime(time);
+
+ _handleTimeDelta = true;
+
+ return;
+ }
+
+ // Special case of first and last cities
+ if (city == kParis || city == kConstantinople) {
+ showFrame(kOverlayTooltip, (city == kParis) ? kTooltipRewindParis : kTooltipForwardConstantinople, true);
+ return;
+ }
+
+ showFrame(kOverlayTooltip, (_time <= time) ? _cityButtonsInfo[city].forward : _cityButtonsInfo[city].rewind, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound / Brightness
+//////////////////////////////////////////////////////////////////////////
+
+// Get current volume (converted internal ScummVM value)
+uint32 Menu::getVolume() const {
+ return getState()->volume;
+}
+
+// Set the volume (converts to ScummVM values)
+void Menu::setVolume(uint32 volume) const {
+ getState()->volume = volume;
+
+ // Clamp volume
+ uint32 value = volume * Audio::Mixer::kMaxMixerVolume / 7;
+
+ if (value > Audio::Mixer::kMaxMixerVolume)
+ value = Audio::Mixer::kMaxMixerVolume;
+
+ _engine->_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, (int32)value);
+}
+
+uint32 Menu::getBrightness() const {
+ return getState()->brightness;
+}
+
+void Menu::setBrightness(uint32 brightness) const {
+ getState()->brightness = brightness;
+
+ // TODO reload cursor & font with adjusted brightness
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/menu.h b/engines/lastexpress/game/menu.h
new file mode 100644
index 0000000000..765a611c41
--- /dev/null
+++ b/engines/lastexpress/game/menu.h
@@ -0,0 +1,210 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_MENU_H
+#define LASTEXPRESS_MENU_H
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/hashmap.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Scene;
+class SceneHotspot;
+
+class Clock;
+class TrainLine;
+
+class Menu : public EventHandler {
+public:
+ Menu(LastExpressEngine *engine);
+ ~Menu();
+
+ void show(bool doSavegame, SavegameType type, uint32 value);
+
+ // Event handling
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ bool isShown() const { return _isShowingMenu; }
+
+ GameId getGameId() const { return _gameId; }
+
+private:
+ // Start menu events
+ enum StartMenuAction {
+ kMenuContinue = 1,
+ kMenuCredits = 2,
+ kMenuQuitGame = 3,
+ kMenuCase4 = 4,
+ kMenuSwitchSaveGame = 6,
+ kMenuRewindGame = 7,
+ kMenuForwardGame = 8,
+ kMenuParis = 10,
+ kMenuStrasBourg = 11,
+ kMenuMunich = 12,
+ kMenuVienna = 13,
+ kMenuBudapest = 14,
+ kMenuBelgrade = 15,
+ kMenuConstantinople = 16,
+ kMenuDecreaseVolume = 17,
+ kMenuIncreaseVolume = 18,
+ kMenuDecreaseBrightness = 19,
+ kMenuIncreaseBrightness = 20
+ };
+
+ // City buttons
+ enum CityButton {
+ kParis = 0,
+ kStrasbourg = 1,
+ kMunich = 2,
+ kVienna = 3,
+ kBudapest = 4,
+ kBelgrade = 5,
+ kConstantinople = 6
+ };
+
+ // Start menu overlay elements
+ enum StartMenuOverlay {
+ kOverlayTooltip, // 0
+ kOverlayEggButtons,
+ kOverlayButtons,
+ kOverlayAcorn,
+ kOverlayCity1,
+ kOverlayCity2, // 5
+ kOverlayCity3,
+ kOverlayCredits
+ };
+
+ LastExpressEngine *_engine;
+
+ // Sequences
+ Sequence *_seqTooltips;
+ Sequence *_seqEggButtons;
+ Sequence *_seqButtons;
+ Sequence *_seqAcorn;
+ Sequence *_seqCity1;
+ Sequence *_seqCity2;
+ Sequence *_seqCity3;
+ Sequence *_seqCredits;
+
+ GameId _gameId;
+
+ // Indicator to know if we need to show the start animation when showMenu is called
+ bool _hasShownStartScreen;
+ bool _hasShownIntro;
+
+ bool _isShowingCredits;
+ bool _isGameStarted;
+ bool _isShowingMenu;
+
+
+ uint16 _creditsSequenceIndex;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Event handling
+ uint32 _checkHotspotsTicks;
+ Common::EventType _mouseFlags;
+ SceneHotspot *_lastHotspot;
+
+ void init(bool doSavegame, SavegameType type, uint32 value);
+ void setup();
+ bool handleEvent(StartMenuAction action, Common::EventType type);
+ void checkHotspots();
+ void setLogicEventHandlers();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Game-related
+ void startGame();
+ void switchGame();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Overlays & elements
+ Clock *_clock;
+ TrainLine *_trainLine;
+
+ struct MenuOverlays_EqualTo {
+ bool operator()(const StartMenuOverlay &x, const StartMenuOverlay &y) const { return x == y; }
+ };
+
+ struct MenuOverlays_Hash {
+ uint operator()(const StartMenuOverlay &x) const { return x; }
+ };
+
+ typedef Common::HashMap<StartMenuOverlay, SequenceFrame *, MenuOverlays_Hash, MenuOverlays_EqualTo> MenuFrames;
+
+ MenuFrames _frames;
+
+ void hideOverlays();
+ void showFrame(StartMenuOverlay overlay, int index, bool redraw);
+
+ void clear();
+
+ // TODO: remove?
+ void moveToCity(CityButton city, bool clicked);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Misc
+ Common::String getAcornSequenceName(GameId id) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Time
+ uint32 _currentTime; // current game time
+ uint32 _lowerTime; // lower time value
+ uint32 _time;
+
+ uint32 _currentIndex; // current savegame entry
+ uint32 _index;
+ uint32 _lastIndex;
+ uint32 _delta;
+ bool _handleTimeDelta;
+
+ void initTime(SavegameType type, uint32 val);
+ void updateTime(uint32 time);
+ void adjustTime();
+ void adjustIndex(uint32 time1, uint32 time2, bool searchEntry);
+ void goToTime(uint32 time);
+ void setTime();
+ void forwardTime();
+ void rewindTime();
+ bool hasTimeDelta() { return (_currentTime - _time) >= 1; }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sound/Brightness related
+ uint32 getVolume() const;
+ void setVolume(uint32 volume) const;
+ uint32 getBrightness() const;
+ void setBrightness(uint32 brightness) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MENU_H
diff --git a/engines/lastexpress/game/object.cpp b/engines/lastexpress/game/object.cpp
new file mode 100644
index 0000000000..4f296debcb
--- /dev/null
+++ b/engines/lastexpress/game/object.cpp
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/object.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Common::String Objects::Object::toString() {
+ return Common::String::format("{ %s - %d - %d - %d - %d }", ENTITY_NAME(entity), location, cursor, cursor2, location2);
+}
+
+Objects::Objects(LastExpressEngine *engine) : _engine(engine) {}
+
+const Objects::Object Objects::get(ObjectIndex index) const {
+ if (index >= kObjectMax)
+ error("Objects::get - internal error: invalid object index (%d)", index);
+
+ return _objects[index];
+}
+
+void Objects::update(ObjectIndex index, EntityIndex entity, ObjectLocation location, CursorStyle cursor, CursorStyle cursor2) {
+ if (index >= kObjectMax)
+ return;
+
+ Object *object = &_objects[index];
+
+ // Store original location
+ ObjectLocation original_location = object->location;
+
+ // Update entity
+ object->entity = entity;
+ object->location = location;
+
+ if (cursor != kCursorKeepValue || cursor2 != kCursorKeepValue) {
+ if (cursor != kCursorKeepValue)
+ object->cursor = cursor;
+ if (cursor2 != kCursorKeepValue)
+ object->cursor2 = cursor2;
+
+ getLogic()->updateCursor();
+ }
+
+ getFlags()->flag_3 = true;
+
+ // Compartments
+ if (original_location != location && (original_location == kObjectLocation2 || location == kObjectLocation2))
+ if ((index >= kObjectCompartment1 && index <= kObjectCompartment8)
+ || (index >= kObjectCompartmentA && index <= kObjectCompartmentF)) {
+ getScenes()->updateDoorsAndClock();
+ }
+}
+
+void Objects::updateLocation2(ObjectIndex index, ObjectLocation location2) {
+ if (index >= kObjectMax)
+ return;
+
+ _objects[index].location2 = location2;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void Objects::saveLoadWithSerializer(Common::Serializer &s) {
+ for (int i = 0; i < ARRAYSIZE(_objects); i++)
+ _objects[i].saveLoadWithSerializer(s);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String Objects::toString() {
+ Common::String ret = "";
+
+ for (int i = 0; i < ARRAYSIZE(_objects); i++)
+ ret += Common::String::format("%d : %s\n", i, _objects[i].toString().c_str());
+
+ return ret;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/object.h b/engines/lastexpress/game/object.h
new file mode 100644
index 0000000000..96af4f07a6
--- /dev/null
+++ b/engines/lastexpress/game/object.h
@@ -0,0 +1,92 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_OBJECT_H
+#define LASTEXPRESS_OBJECT_H
+
+#include "lastexpress/shared.h"
+
+#include "common/serializer.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Objects : Common::Serializable {
+public:
+
+ struct Object : Common::Serializable { // All fields should be saved as bytes
+ EntityIndex entity;
+ ObjectLocation location;
+ CursorStyle cursor;
+ CursorStyle cursor2;
+ ObjectLocation location2;
+
+ Object() {
+ entity = kEntityPlayer;
+ location = kObjectLocationNone;
+ cursor = kCursorHandKnock;
+ cursor2 = kCursorHandKnock;
+ location2 = kObjectLocationNone;
+ }
+
+ Common::String toString();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsByte(entity);
+ s.syncAsByte(location);
+ s.syncAsByte(cursor);
+ s.syncAsByte(cursor2);
+ s.syncAsByte(location2);
+ }
+ };
+
+ Objects(LastExpressEngine *engine);
+
+ const Object get(ObjectIndex index) const;
+ void update(ObjectIndex index, EntityIndex entity, ObjectLocation location, CursorStyle cursor, CursorStyle cursor2);
+ void updateLocation2(ObjectIndex index, ObjectLocation location2);
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ LastExpressEngine *_engine;
+
+ Object _objects[kObjectMax];
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_OBJECT_H
diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp
new file mode 100644
index 0000000000..2d88c2dca4
--- /dev/null
+++ b/engines/lastexpress/game/savegame.cpp
@@ -0,0 +1,569 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/savegame.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/debug.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+#include "common/file.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+// Names of savegames
+static const struct {
+ const char *saveFile;
+} gameInfo[6] = {
+ {"blue.egg"},
+ {"red.egg"},
+ {"green.egg"},
+ {"purple.egg"},
+ {"teal.egg"},
+ {"gold.egg"}
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Constructors
+//////////////////////////////////////////////////////////////////////////
+
+SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) {
+}
+
+SaveLoad::~SaveLoad() {
+ clear(true);
+
+ //Zero passed pointers
+ _engine = NULL;
+}
+
+void SaveLoad::initStream() {
+ delete _savegame;
+ _savegame = new SavegameStream();
+}
+
+void SaveLoad::flushStream(GameId id) {
+ Common::OutSaveFile *save = openForSaving(id);
+ if (!save)
+ error("SaveLoad::flushStream: cannot open savegame (%s)!", getFilename(id).c_str());
+
+ if (!_savegame)
+ error("SaveLoad::flushStream: savegame stream is invalid");
+
+ save->write(_savegame->getData(), (uint32)_savegame->size());
+
+ delete save;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Init
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::create(GameId id) {
+ initStream();
+
+ Common::Serializer ser(NULL, _savegame);
+ SavegameMainHeader header;
+ header.saveLoadWithSerializer(ser);
+
+ flushStream(id);
+}
+
+uint32 SaveLoad::init(GameId id, bool resetHeaders) {
+ initStream();
+
+ // Load game data
+ loadStream(id);
+
+ // Get the main header
+ Common::Serializer ser(_savegame, NULL);
+ SavegameMainHeader mainHeader;
+ mainHeader.saveLoadWithSerializer(ser);
+ if (!mainHeader.isValid())
+ error("SaveLoad::init - Savegame seems to be corrupted (invalid header)");
+
+ // Reset cached entry headers if needed
+ if (resetHeaders) {
+ clear();
+
+ SavegameEntryHeader *entryHeader = new SavegameEntryHeader();
+ entryHeader->time = kTimeCityParis;
+ entryHeader->chapter = kChapter1;
+
+ _gameHeaders.push_back(entryHeader);
+ }
+
+ // Read the list of entry headers
+ if (_savegame->size() > 32) {
+ while (_savegame->pos() < _savegame->size() && !_savegame->eos() && !_savegame->err()) {
+
+ // Update sound queue while we go through the savegame
+ getSound()->updateQueue();
+
+ SavegameEntryHeader *entry = new SavegameEntryHeader();
+ entry->saveLoadWithSerializer(ser);
+
+ if (!entry->isValid())
+ break;
+
+ _gameHeaders.push_back(entry);
+
+ _savegame->seek(entry->offset, SEEK_CUR);
+ }
+ }
+
+ // return the index to the current save game entry (we store count + 1 entries, so we're good)
+ return mainHeader.count;
+}
+
+void SaveLoad::loadStream(GameId id) {
+ Common::InSaveFile *save = openForLoading(id);
+ if (save->size() < 32)
+ error("SaveLoad::init - Savegame seems to be corrupted (not enough data: %i bytes)", save->size());
+
+ if (!_savegame)
+ error("SaveLoad::loadStream: savegame stream is invalid");
+
+ // Load all savegame data
+ uint8* buf = new uint8[8192];
+ while (!save->eos() && !save->err()) {
+ _engine->pollEvents();
+
+ uint32 count = save->read(buf, sizeof(buf));
+ if (count) {
+ uint32 w = _savegame->write(buf, count);
+ assert (w == count);
+ }
+ }
+
+ if (save->err())
+ error("SaveLoad::init - Error reading savegame");
+
+ delete[] buf;
+ delete save;
+
+ // Move back to the beginning of the stream
+ _savegame->seek(0);
+}
+
+void SaveLoad::clear(bool clearStream) {
+ for (uint i = 0; i < _gameHeaders.size(); i++)
+ SAFE_DELETE(_gameHeaders[i]);
+
+ _gameHeaders.clear();
+
+ if (clearStream)
+ SAFE_DELETE(_savegame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Save & Load
+//////////////////////////////////////////////////////////////////////////
+
+// Load game
+void SaveLoad::loadGame(GameId id) {
+ // Rewind current savegame
+ _savegame->seek(0);
+
+ // Validate main header
+ SavegameMainHeader header;
+ if (!loadMainHeader(_savegame, &header)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::saveGame - Cannot load main header: %s", getFilename(getMenu()->getGameId()).c_str());
+ return;
+ }
+
+ // Load the last entry
+ _savegame->seek(header.offsetEntry);
+
+ SavegameType type = kSavegameTypeIndex;
+ EntityIndex entity = kEntityPlayer;
+ uint32 val = 0;
+ readEntry(&type, &entity, &val, header.keepIndex == 1);
+
+ // Setup last loading time
+ _gameTicksLastSavegame = getState()->timeTicks;
+
+ if (header.keepIndex) {
+ getSound()->clearQueue();
+
+ readEntry(&type, &entity, &val, false);
+ }
+
+ getEntities()->reset();
+ getEntities()->setup(false, entity);
+}
+
+// Load a specific game entry
+void SaveLoad::loadGame(GameId id, uint32 index) {
+ error("SaveLoad::loadGame: not implemented! (only loading the last entry is working for now)");
+}
+
+// Save game
+void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
+ if (getState()->scene <= kSceneIntro)
+ return;
+
+ // Validate main header
+ SavegameMainHeader header;
+ if (!loadMainHeader(_savegame, &header)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::saveGame - Cannot load main header: %s", getFilename(getMenu()->getGameId()).c_str());
+ return;
+ }
+
+ if (!_savegame)
+ error("SaveLoad::saveGame: savegame stream is invalid");
+
+ // Validate the current entry if it exists
+ if (header.count > 0) {
+ _savegame->seek(header.offsetEntry);
+
+ // Load entry header
+ SavegameEntryHeader entry;
+ Common::Serializer ser(_savegame, NULL);
+ entry.saveLoadWithSerializer(ser);
+
+ if (!entry.isValid()) {
+ warning("SaveLoad::saveGame: Invalid entry. This savegame might be corrupted!");
+ _savegame->seek(header.offset);
+ } else if (getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) {
+ // Not ready to save a game, skipping!
+ return;
+ } else if ((type == kSavegameTypeTime || type == kSavegameTypeEvent)
+ && (entry.type == kSavegameTypeTickInterval && (getState()->time - entry.time) < 450)) {
+ _savegame->seek(header.offsetEntry);
+ --header.count;
+ } else {
+ _savegame->seek(header.offset);
+ }
+ } else {
+ // Seek to the next savegame entry
+ _savegame->seek(header.offset);
+ }
+
+ if (type != kSavegameTypeEvent2 && type != kSavegameTypeAuto)
+ header.offsetEntry = (uint32)_savegame->pos();
+
+ // Write the savegame entry
+ writeEntry(type, entity, value);
+
+ if (!header.keepIndex)
+ ++header.count;
+
+ if (type == kSavegameTypeEvent2 || type == kSavegameTypeAuto) {
+ header.keepIndex = 1;
+ } else {
+ header.keepIndex = 0;
+ header.offset = (uint32)_savegame->pos();
+
+ // Save ticks
+ _gameTicksLastSavegame = getState()->timeTicks;
+ }
+
+ // Validate the main header
+ if (!header.isValid())
+ error("SaveLoad::saveGame: main game header is invalid!");
+
+ // Write the main header
+ _savegame->seek(0);
+ Common::Serializer ser(NULL, _savegame);
+ header.saveLoadWithSerializer(ser);
+
+ flushStream(getMenu()->getGameId());
+}
+
+void SaveLoad::saveVolumeBrightness() {
+ warning("SaveLoad::saveVolumeBrightness: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Headers
+//////////////////////////////////////////////////////////////////////////
+bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header) {
+ if (!stream)
+ return false;
+
+ // Check there is enough data (32 bytes)
+ if (stream->size() < 32) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes)!", stream->size());
+ return false;
+ }
+
+ // Rewind stream
+ stream->seek(0);
+
+ Common::Serializer ser(stream, NULL);
+ header->saveLoadWithSerializer(ser);
+
+ // Validate the header
+ if (!header->isValid()) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header!");
+ return false;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entries
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
+#define WRITE_ENTRY(name, func, val) { \
+ uint32 _prevPosition = (uint32)_savegame->pos(); \
+ func; \
+ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
+ debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \
+ if (_count != val)\
+ error("SaveLoad::writeEntry: Number of bytes written (%d) differ from expected count (%d)", _count, val); \
+}
+
+ if (!_savegame)
+ error("SaveLoad::writeEntry: savegame stream is invalid");
+
+ SavegameEntryHeader header;
+
+ header.type = type;
+ header.time = (uint32)getState()->time;
+ header.chapter = getProgress().chapter;
+ header.value = value;
+
+ // Save position
+ uint32 originalPosition = (uint32)_savegame->pos();
+
+ // Write header
+ Common::Serializer ser(NULL, _savegame);
+ header.saveLoadWithSerializer(ser);
+
+ // Write game data
+ WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4);
+ WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4);
+ WRITE_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4);
+ WRITE_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000);
+ WRITE_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2);
+ WRITE_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128);
+ WRITE_ENTRY("events", getState()->syncEvents(ser), 512);
+ WRITE_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32);
+ WRITE_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128);
+ WRITE_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40);
+ WRITE_ENTRY("sound", getSound()->saveLoadWithSerializer(ser), 3 * 4 + getSound()->count() * 64);
+ WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 128 * 16 + 4 + getSavePoints()->count() * 16);
+
+ header.offset = (uint32)_savegame->pos() - (originalPosition + 32);
+
+ // Add padding if necessary
+ while (header.offset & 0xF) {
+ _savegame->writeByte(0);
+ header.offset++;
+ }
+
+ // Save end position
+ uint32 endPosition = (uint32)_savegame->pos();
+
+ // Validate entry header
+ if (!header.isValid())
+ error("SaveLoad::writeEntry: entry header is invalid");
+
+ // Save the header with the updated info
+ _savegame->seek(originalPosition);
+ header.saveLoadWithSerializer(ser);
+
+ // Move back to the end of the entry
+ _savegame->seek(endPosition);
+}
+
+void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex) {
+#define LOAD_ENTRY(name, func, val) { \
+ uint32 _prevPosition = (uint32)_savegame->pos(); \
+ func; \
+ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
+ debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \
+ if (_count != val) \
+ error("SaveLoad::readEntry: Number of bytes read (%d) differ from expected count (%d)", _count, val); \
+}
+
+#define LOAD_ENTRY_ONLY(name, func) { \
+ uint32 _prevPosition = (uint32)_savegame->pos(); \
+ func; \
+ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
+ debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \
+}
+
+ if (!type || !entity || !val)
+ error("SaveLoad::readEntry: Invalid parameters passed!");
+
+ // Load entry header
+ SavegameEntryHeader entry;
+ Common::Serializer ser(_savegame, NULL);
+ entry.saveLoadWithSerializer(ser);
+
+ if (!entry.isValid())
+ error("SaveLoad::readEntry: entry header is invalid!");
+
+ // Init type, entity & value
+ *type = entry.type;
+ *val = entry.value;
+
+ // Save position
+ uint32 originalPosition = (uint32)_savegame->pos();
+
+ // Load game data
+ LOAD_ENTRY("entity index", ser.syncAsUint32LE(*entity), 4);
+ LOAD_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4);
+ LOAD_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4);
+ LOAD_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000);
+ LOAD_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2);
+ LOAD_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128);
+ LOAD_ENTRY("events", getState()->syncEvents(ser), 512);
+ LOAD_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32);
+ LOAD_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128);
+ LOAD_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40);
+ LOAD_ENTRY_ONLY("sound", getSound()->saveLoadWithSerializer(ser));
+ LOAD_ENTRY_ONLY("savepoints", getSavePoints()->saveLoadWithSerializer(ser));
+
+ // Update chapter
+ getProgress().chapter = entry.chapter;
+
+ // Skip padding
+ uint32 offset = _savegame->pos() - originalPosition;
+ if (offset & 0xF) {
+ _savegame->seek((~offset & 0xF) + 1, SEEK_SET);
+ }
+}
+
+SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) {
+ if (index >= _gameHeaders.size())
+ error("SaveLoad::getEntry: invalid index (was:%d, max:%d)", index, _gameHeaders.size() - 1);
+
+ return _gameHeaders[index];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+
+// Check if a specific savegame exists
+bool SaveLoad::isSavegamePresent(GameId id) {
+ if (g_system->getSavefileManager()->listSavefiles(getFilename(id)).size() == 0)
+ return false;
+
+ return true;
+}
+
+// Check if the game has been started in the specific savegame
+bool SaveLoad::isSavegameValid(GameId id) {
+ if (!isSavegamePresent(id)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getFilename(id).c_str());
+ return false;
+ }
+
+ SavegameMainHeader header;
+
+ Common::InSaveFile *save = openForLoading(id);
+ bool isHeaderValid = loadMainHeader(save, &header);
+ delete save;
+
+ return isHeaderValid;
+}
+
+bool SaveLoad::isGameFinished(uint32 menuIndex, uint32 savegameIndex) {
+ SavegameEntryHeader *data = getEntry(menuIndex);
+
+ if (savegameIndex != menuIndex)
+ return false;
+
+ if (data->type != kSavegameTypeEvent)
+ return false;
+
+ return (data->value == kEventAnnaKilled
+ || data->value == kEventKronosHostageAnnaNoFirebird
+ || data->value == kEventKahinaPunchBaggageCarEntrance
+ || data->value == kEventKahinaPunchBlue
+ || data->value == kEventKahinaPunchYellow
+ || data->value == kEventKahinaPunchSalon
+ || data->value == kEventKahinaPunchKitchen
+ || data->value == kEventKahinaPunchBaggageCar
+ || data->value == kEventKahinaPunchCar
+ || data->value == kEventKahinaPunchSuite4
+ || data->value == kEventKahinaPunchRestaurant
+ || data->value == kEventKahinaPunch
+ || data->value == kEventKronosGiveFirebird
+ || data->value == kEventAugustFindCorpse
+ || data->value == kEventMertensBloodJacket
+ || data->value == kEventMertensCorpseFloor
+ || data->value == kEventMertensCorpseBed
+ || data->value == kEventCoudertBloodJacket
+ || data->value == kEventGendarmesArrestation
+ || data->value == kEventAbbotDrinkGiveDetonator
+ || data->value == kEventMilosCorpseFloor
+ || data->value == kEventLocomotiveAnnaStopsTrain
+ || data->value == kEventTrainStopped
+ || data->value == kEventCathVesnaRestaurantKilled
+ || data->value == kEventCathVesnaTrainTopKilled
+ || data->value == kEventLocomotiveConductorsDiscovered
+ || data->value == kEventViennaAugustUnloadGuns
+ || data->value == kEventViennaKronosFirebird
+ || data->value == kEventVergesAnnaDead
+ || data->value == kEventTrainExplosionBridge
+ || data->value == kEventKronosBringNothing);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private methods
+//////////////////////////////////////////////////////////////////////////
+
+// Get the file name from the savegame ID
+Common::String SaveLoad::getFilename(GameId id) {
+ if (id >= 6)
+ error("SaveLoad::getName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id);
+
+ return gameInfo[id].saveFile;
+}
+
+Common::InSaveFile *SaveLoad::openForLoading(GameId id) {
+ Common::InSaveFile *load = g_system->getSavefileManager()->openForLoading(getFilename(id));
+
+ if (!load)
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForLoading - Cannot open savegame for loading: %s", getFilename(id).c_str());
+
+ return load;
+}
+
+Common::OutSaveFile *SaveLoad::openForSaving(GameId id) {
+ Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id));
+
+ if (!save)
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForSaving - Cannot open savegame for writing: %s", getFilename(id).c_str());
+
+ return save;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h
new file mode 100644
index 0000000000..5c0e0e9890
--- /dev/null
+++ b/engines/lastexpress/game/savegame.h
@@ -0,0 +1,289 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SAVELOAD_H
+#define LASTEXPRESS_SAVELOAD_H
+
+/*
+ Savegame format
+ ---------------
+
+ header: 32 bytes
+ uint32 {4} - signature: 0x12001200
+ uint32 {4} - chapter - needs to be [0; 5]
+ uint32 {4} - time - needs to be >= 32 [1061100; timeMax]
+ uint32 {4} - ?? needs to be >= 32
+ uint32 {4} - ?? needs to be = 1
+ uint32 {4} - Brightness (needs to be [0-6])
+ uint32 {4} - Volume (needs to be [0-7])
+ uint32 {4} - ?? needs to be = 9
+
+ Game data Format
+ -----------------
+
+ uint32 {4} - entity
+ uint32 {4} - current time
+ uint32 {4} - time delta (how much a tick is in "real" time)
+ uint32 {4} - time ticks
+ uint32 {4} - scene Index max: 2500
+ byte {1} - use backup scene
+ uint32 {4} - backup Scene Index 1 max: 2500
+ uint32 {4} - backup Scene Index 2 max: 2500
+ uint32 {4} - selected inventory item max: 32
+ uint32 {4*100*10} - positions (by car)
+ uint32 {4*16} - compartments
+ uint32 {4*16} - compartments ??
+ uint32 {4*128} - game progress
+ byte {512} - game events
+ byte {7*32} - inventory
+ byte {5*128} - objects
+ byte {1262*40} - entities (characters and train entities)
+
+ uint32 {4} - sound queue state
+ uint32 {4} - ??
+ uint32 {4} - number of sound entries
+ byte {count*68} - sound entries
+
+ byte {16*128} - save point data
+ uint32 {4} - number of save points (max: 128)
+ byte {count*16} - save points
+
+ ... more unknown stuff
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "common/memstream.h"
+
+namespace LastExpress {
+
+// Savegame signatures
+#define SAVEGAME_SIGNATURE 0x12001200
+#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660
+
+class LastExpressEngine;
+
+class SaveLoad {
+public:
+ SaveLoad(LastExpressEngine *engine);
+ ~SaveLoad();
+
+ // Init
+ void create(GameId id);
+ void clear(bool clearStream = false);
+ uint32 init(GameId id, bool resetHeaders);
+
+ // Save & Load
+ void loadGame(GameId id);
+ void loadGame(GameId id, uint32 index);
+ void saveGame(SavegameType type, EntityIndex entity, uint32 value);
+
+ void loadVolumeBrightness();
+ void saveVolumeBrightness();
+
+ // Getting information
+ static bool isSavegamePresent(GameId id);
+ static bool isSavegameValid(GameId id);
+
+ bool isGameFinished(uint32 menuIndex, uint32 savegameIndex);
+
+ // Accessors
+ uint32 getTime(uint32 index) { return getEntry(index)->time; }
+ ChapterIndex getChapter(uint32 index) { return getEntry(index)->chapter; }
+ uint32 getValue(uint32 index) { return getEntry(index)->value; }
+ uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; }
+
+private:
+ class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
+ public:
+ SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES),
+ _eos(false) {}
+
+ int32 pos() const { return MemoryWriteStreamDynamic::pos(); }
+ int32 size() const { return MemoryWriteStreamDynamic::size(); }
+ bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); }
+ bool eos() const { return _eos; }
+ uint32 read(void *dataPtr, uint32 dataSize) {
+ if ((int32)dataSize > size() - pos()) {
+ dataSize = size() - pos();
+ _eos = true;
+ }
+ memcpy(dataPtr, getData() + pos(), dataSize);
+
+ seek(dataSize, SEEK_CUR);
+
+ return dataSize;
+ }
+ private:
+ bool _eos;
+ };
+
+ LastExpressEngine *_engine;
+
+ struct SavegameMainHeader : Common::Serializable {
+ uint32 signature;
+ uint32 count;
+ uint32 offset;
+ uint32 offsetEntry;
+ uint32 keepIndex;
+ int32 brightness;
+ int32 volume;
+ uint32 field_1C;
+
+ SavegameMainHeader() {
+ signature = SAVEGAME_SIGNATURE;
+ count = 0;
+ offset = 32;
+ offsetEntry = 32;
+ keepIndex = 0;
+ brightness = 3;
+ volume = 7;
+ field_1C = 9;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(signature);
+ s.syncAsUint32LE(count);
+ s.syncAsUint32LE(offset);
+ s.syncAsUint32LE(offsetEntry);
+ s.syncAsUint32LE(keepIndex);
+ s.syncAsUint32LE(brightness);
+ s.syncAsUint32LE(volume);
+ s.syncAsUint32LE(field_1C);
+ }
+
+ bool isValid() {
+ if (signature != SAVEGAME_SIGNATURE)
+ return false;
+
+ /* Check not needed as it can never be < 0
+ if (header.chapter < 0)
+ return false;*/
+
+ if (offset < 32)
+ return false;
+
+ if (offsetEntry < 32)
+ return false;
+
+ if (keepIndex != 1 && keepIndex != 0)
+ return false;
+
+ if (brightness < 0 || brightness > 6)
+ return false;
+
+ if (volume < 0 || volume > 7)
+ return false;
+
+ if (field_1C != 9)
+ return false;
+
+ return true;
+ }
+ };
+
+ struct SavegameEntryHeader : Common::Serializable {
+ uint32 signature;
+ SavegameType type;
+ uint32 time;
+ int offset;
+ ChapterIndex chapter;
+ uint32 value;
+ int field_18;
+ int field_1C;
+
+ SavegameEntryHeader() {
+ signature = SAVEGAME_ENTRY_SIGNATURE;
+ type = kSavegameTypeIndex;
+ time = kTimeNone;
+ offset = 0;
+ chapter = kChapterAll;
+ value = 0;
+ field_18 = 0;
+ field_1C = 0;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(signature);
+ s.syncAsUint32LE(type);
+ s.syncAsUint32LE(time);
+ s.syncAsUint32LE(offset);
+ s.syncAsUint32LE(chapter);
+ s.syncAsUint32LE(value);
+ s.syncAsUint32LE(field_18);
+ s.syncAsUint32LE(field_1C);
+ }
+
+ bool isValid() {
+ if (signature != SAVEGAME_ENTRY_SIGNATURE)
+ return false;
+
+ if (type < kSavegameTypeTime || type > kSavegameTypeTickInterval)
+ return false;
+
+ if (time < kTimeStartGame || time > kTimeCityConstantinople)
+ return false;
+
+ if (offset <= 0 || offset & 15)
+ return false;
+
+ /* No check for < 0, as it cannot happen normaly */
+ if (chapter == 0)
+ return false;
+
+ return true;
+ }
+ };
+
+ SavegameStream *_savegame;
+ Common::Array<SavegameEntryHeader *> _gameHeaders;
+ uint32 _gameTicksLastSavegame;
+
+ // Headers
+ static bool loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header);
+
+ // Entries
+ void writeEntry(SavegameType type, EntityIndex entity, uint32 val);
+ void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex);
+
+ SavegameEntryHeader *getEntry(uint32 index);
+
+ // Opening save files
+ static Common::String getFilename(GameId id);
+ static Common::InSaveFile *openForLoading(GameId id);
+ static Common::OutSaveFile *openForSaving(GameId id);
+
+ // Savegame stream
+ void initStream();
+ void loadStream(GameId id);
+ void flushStream(GameId id);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SAVELOAD_H
diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp
new file mode 100644
index 0000000000..30467f8368
--- /dev/null
+++ b/engines/lastexpress/game/savepoint.cpp
@@ -0,0 +1,296 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/savepoint.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+
+namespace LastExpress {
+
+SavePoints::SavePoints(LastExpressEngine *engine) : _engine(engine) {
+ for (int i = 0; i < 40; i++)
+ _callbacks[i] = NULL;
+}
+
+SavePoints::~SavePoints() {
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savepoints
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param) {
+ if (_savepoints.size() >= _savePointsMaxSize)
+ return;
+
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ point.param.intValue = param;
+
+ _savepoints.push_back(point);
+}
+
+void SavePoints::push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) {
+ if (_savepoints.size() >= _savePointsMaxSize)
+ return;
+
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ strcpy((char *)&point.param.charValue, param);
+
+ _savepoints.push_back(point);
+}
+
+SavePoint SavePoints::pop() {
+ SavePoint point = _savepoints.front();
+ _savepoints.pop_front();
+ return point;
+}
+
+
+void SavePoints::pushAll(EntityIndex entity, ActionIndex action, uint32 param) {
+ for (uint32 index = 1; index < 40; index++) {
+ if ((EntityIndex)index != entity)
+ push(entity, (EntityIndex)index, action, param);
+ }
+}
+
+// Process all savepoints
+void SavePoints::process() {
+ while (_savepoints.size() > 0 && getFlags()->isGameRunning) {
+ SavePoint savepoint = pop();
+
+ // If this is a data savepoint, update the entity
+ // otherwise, execute the callback
+ if (!updateEntityFromData(savepoint)) {
+
+ // Call requested callback
+ Entity::Callback *callback = getCallback(savepoint.entity1);
+ if (callback && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s", ENTITY_NAME(savepoint.entity1), ACTION_NAME(savepoint.action), ENTITY_NAME(savepoint.entity2));
+ (*callback)(savepoint);
+ }
+ }
+ }
+}
+
+void SavePoints::reset() {
+ _savepoints.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Data
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::addData(EntityIndex entity, ActionIndex action, uint32 param) {
+ if (_data.size() >= _savePointsMaxSize)
+ return;
+
+ SavePointData data;
+ data.entity1 = entity;
+ data.action = action;
+ data.param = param;
+
+ _data.push_back(data);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) {
+ if (index >= 40)
+ error("SavePoints::setCallback - attempting to use an invalid entity index. Valid values 0-39, was %d", index);
+
+ if (!callback || !callback->isValid())
+ error("SavePoints::setCallback - attempting to set an invalid callback for entity %s", ENTITY_NAME(index));
+
+ _callbacks[index] = callback;
+}
+
+Entity::Callback *SavePoints::getCallback(EntityIndex index) const {
+ if (index >= 40)
+ error("SavePoints::getCallback - attempting to use an invalid entity index. Valid values 0-39, was %d", index);
+
+ return _callbacks[index];
+}
+
+void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param) const {
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ point.param.intValue = param;
+
+ Entity::Callback *callback = getCallback(entity1);
+ if (callback != NULL && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%d", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param);
+ (*callback)(point);
+ }
+}
+
+void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const {
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ strcpy((char *)&point.param.charValue, param);
+
+ Entity::Callback *callback = getCallback(entity1);
+ if (callback != NULL && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%s", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param);
+ (*callback)(point);
+ }
+}
+
+void SavePoints::callAndProcess() {
+ SavePoint savepoint; // empty parameters
+
+ // We ignore the kEntityPlayer callback in the list
+ EntityIndex index = kEntityAnna;
+
+ // Call all callbacks with empty parameters
+ bool isRunning = getFlags()->isGameRunning;
+ while (isRunning) {
+
+ Entity::Callback *callback = getCallback(index);
+ if (callback != NULL && callback->isValid()) {
+ (*callback)(savepoint);
+ isRunning = getFlags()->isGameRunning;
+ }
+
+ index = (EntityIndex)(index + 1);
+
+ // Process all savepoints when done
+ if (index >= 40) {
+ if (isRunning)
+ process();
+
+ return;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+bool SavePoints::updateEntityFromData(const SavePoint &savepoint) {
+ for (int i = 0; i < (int)_data.size(); i++) {
+
+ // Not a data savepoint!
+ if (!_data[i].entity1)
+ return false;
+
+ // Found our data!
+ if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) {
+ debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param);
+
+ // the SavePoint param value is the index of the entity call parameter to update
+ getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::saveLoadWithSerializer(Common::Serializer &s) {
+
+ // Serialize savepoint data
+ uint32 dataSize = (s.isLoading() ? _savePointsMaxSize : _data.size());
+ for (uint i = 0; i < dataSize; i++) {
+ if (s.isLoading()) {
+ SavePointData data;
+ _data.push_back(data);
+ }
+
+ s.syncAsUint32LE(_data[i].entity1);
+ s.syncAsUint32LE(_data[i].action);
+ s.syncAsUint32LE(_data[i].entity2);
+ s.syncAsUint32LE(_data[i].param);
+ }
+
+ // Skip uninitialized data if any
+ s.skip((_savePointsMaxSize - dataSize) * 16);
+
+ // Number of savepoints
+ uint32 numSavepoints = _savepoints.size();
+ s.syncAsUint32LE(numSavepoints);
+
+ // Savepoints
+ if (s.isLoading()) {
+ for (uint i = 0; i < numSavepoints; i++) {
+ SavePoint point;
+ s.syncAsUint32LE(point.entity1);
+ s.syncAsUint32LE(point.action);
+ s.syncAsUint32LE(point.entity2);
+ s.syncAsUint32LE(point.param.intValue);
+
+ _savepoints.push_back(point);
+
+ if (_savepoints.size() >= _savePointsMaxSize)
+ break;
+ }
+ } else {
+ for (Common::List<SavePoint>::iterator it = _savepoints.begin(); it != _savepoints.end(); ++it) {
+ s.syncAsUint32LE((*it).entity1);
+ s.syncAsUint32LE((*it).action);
+ s.syncAsUint32LE((*it).entity2);
+ s.syncAsUint32LE((*it).param.intValue);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String SavePoints::toString() {
+ Common::String ret = "";
+
+ ret += "Savepoint Data\n";
+ for (uint i = 0; i < _data.size(); i++)
+ ret += _data[i].toString() + "\n";
+
+ ret += "\nSavepoints\n";
+ for (Common::List<SavePoint>::iterator it = _savepoints.begin(); it != _savepoints.end(); ++it)
+ ret += (*it).toString() + "\n";
+
+ return ret;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/savepoint.h b/engines/lastexpress/game/savepoint.h
new file mode 100644
index 0000000000..ca507ab8ab
--- /dev/null
+++ b/engines/lastexpress/game/savepoint.h
@@ -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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SAVEPOINT_H
+#define LASTEXPRESS_SAVEPOINT_H
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/helpers.h"
+
+#include "common/array.h"
+#include "common/list.h"
+#include "common/serializer.h"
+
+/*
+ Savepoint format
+ ----------------
+
+ Save point: max: 127 - FIFO list (ie. goes back and overwrites first save point when full)
+ uint32 {4} - Entity 1
+ uint32 {4} - Action
+ uint32 {4} - Entity 2
+ uint32 {4} - Parameter
+
+ Save point Data
+ uint32 {4} - Entity 1
+ uint32 {4} - Action
+ uint32 {4} - Entity 2
+ uint32 {4} - function pointer to ??
+
+*/
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+struct SavePoint {
+ EntityIndex entity1;
+ ActionIndex action;
+ EntityIndex entity2;
+ union {
+ uint32 intValue;
+ char charValue[5];
+ } param;
+
+ SavePoint() {
+ entity1 = kEntityPlayer;
+ action = kActionNone;
+ entity2 = kEntityPlayer;
+ param.intValue = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format("{ %s - %d - %s - %s }", ENTITY_NAME(entity1), action, ENTITY_NAME(entity2), param.charValue);
+ }
+};
+
+class SavePoints : Common::Serializable {
+private:
+ typedef Common::Functor1<const SavePoint&, void> Callback;
+
+public:
+
+ struct SavePointData {
+ EntityIndex entity1;
+ ActionIndex action;
+ EntityIndex entity2;
+ uint32 param;
+
+ SavePointData() {
+ entity1 = kEntityPlayer;
+ action = kActionNone;
+ entity2 = kEntityPlayer;
+ param = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::format(" { %s - %d - %s - %d }", ENTITY_NAME(entity1), action, ENTITY_NAME(entity2), param);
+ }
+ };
+
+ SavePoints(LastExpressEngine *engine);
+ ~SavePoints();
+
+ // Savepoints
+ void push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0);
+ void push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param);
+ void pushAll(EntityIndex entity, ActionIndex action, uint32 param = 0);
+ void process();
+ void reset();
+
+ // Data
+ void addData(EntityIndex entity, ActionIndex action, uint32 param);
+
+ // Callbacks
+ void setCallback(EntityIndex index, Entity::Callback *callback);
+ Callback *getCallback(EntityIndex entity) const;
+ void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0) const;
+ void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const;
+ void callAndProcess();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+ uint32 count() { return _savepoints.size(); }
+
+private:
+ static const uint32 _savePointsMaxSize = 128;
+
+ LastExpressEngine *_engine;
+
+ Common::List<SavePoint> _savepoints; ///< could be a queue, but we need to be able to iterate on the items
+ Common::Array<SavePointData> _data;
+ Callback *_callbacks[40];
+
+ SavePoint pop();
+ bool updateEntityFromData(const SavePoint &point);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SAVEPOINT_H
diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp
new file mode 100644
index 0000000000..15fe0617d8
--- /dev/null
+++ b/engines/lastexpress/game/scenes.cpp
@@ -0,0 +1,1198 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/scenes.h"
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+SceneManager::SceneManager(LastExpressEngine *engine) : _engine(engine),
+ _flagNoEntity(false), _flagDrawEntities(false), _flagDrawSequences(false), _flagCoordinates(false),
+ _coords(0, 0, 480, 640), _clockHours(NULL), _clockMinutes(NULL) {
+ _sceneLoader = new SceneLoader();
+}
+
+SceneManager::~SceneManager() {
+ // Clear frames
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door)
+ SAFE_DELETE(*door);
+
+ _doors.clear();
+
+ SAFE_DELETE(_clockHours);
+ SAFE_DELETE(_clockMinutes);
+
+ // Clear frame queue
+ _queue.clear();
+
+ SAFE_DELETE(_sceneLoader);
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene cache
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::loadSceneDataFile(ArchiveIndex archive) {
+ // Demo only has CD2TRAIN.DAT file
+ if (_engine->isDemo())
+ archive = kArchiveCd2;
+
+ switch(archive) {
+ case kArchiveCd1:
+ case kArchiveCd2:
+ case kArchiveCd3:
+ if (!_sceneLoader->load(getArchive(Common::String::format("CD%iTRAIN.DAT", archive))))
+ error("SceneManager::loadSceneDataFile: cannot load data file CD%iTRAIN.DAT", archive);
+ break;
+
+ default:
+ case kArchiveAll:
+ error("SceneManager::loadSceneDataFile: Invalid archive index (must be [1-3], was %d", archive);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene loading
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::loadScene(SceneIndex index) {
+ getFlags()->flag_0 = false;
+ getFlags()->flag_4 = true;
+
+ if (getState()->sceneUseBackup) {
+ Scene *scene = getScenes()->get(index);
+
+ if (scene->param3 != 255) {
+ getState()->sceneUseBackup = false;
+ getState()->sceneBackup2 = kSceneNone;
+ }
+ }
+
+ // Save shouldRedraw state and redraw if necessary
+ bool shouldRedraw = getFlags()->shouldRedraw;
+ if (shouldRedraw) {
+ shouldRedraw = false;
+ // TODO check whether we need to do that here
+ askForRedraw();
+ //redrawScreen();
+ }
+
+ // Set the scene
+ setScene(index);
+
+ // TODO Events method call (might be a low level graphic that we don't need)
+
+ if (getFlags()->isGameRunning && getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = shouldRedraw;
+
+ getLogic()->updateCursor();
+}
+
+void SceneManager::loadSceneFromObject(ObjectIndex object, bool alternate) {
+ switch (object) {
+ default:
+ break;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ if (alternate)
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 1) * 2));
+ else
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(38 - (object - 1) * 2));
+ break;
+
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ case kObjectCompartmentG:
+ if (alternate)
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 32) * 2));
+ else
+ loadSceneFromPosition(kCarRedSleeping, (Position)(38 - (object - 32) * 2));
+ break;
+
+ case kObjectCompartment8:
+ case kObjectCompartmentH:
+ loadSceneFromPosition(object == kObjectCompartment8 ? kCarGreenSleeping : kCarRedSleeping, alternate ? 3 : 25);
+ break;
+ }
+}
+
+void SceneManager::loadSceneFromItem(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ // Get the scene index from the item
+ SceneIndex index = getInventory()->get(item)->scene;
+ if (!index)
+ return;
+
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneUseBackup = true;
+ getState()->sceneBackup = getState()->scene;
+ }
+
+ loadScene(index);
+}
+
+void SceneManager::loadSceneFromPosition(CarIndex car, Position position, int param3) {
+ loadScene(getSceneIndexFromPosition(car, position, param3));
+}
+
+void SceneManager::loadSceneFromItemPosition(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ // Check item location
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (!entry->location)
+ return;
+
+ // Reset location
+ entry->location = kObjectLocationNone;
+
+ if (item != kItem3 && item != kItem5 && item != kItem7)
+ return;
+
+ // Set field value
+ CarIndex car = kCarRestaurant;
+ if (item == kItem5) car = kCarRedSleeping;
+ if (item == kItem7) car = kCarGreenSleeping;
+
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, car))
+ return;
+
+ if (getFlags()->flag_0)
+ return;
+
+ // Get current scene position
+ Scene *scene = getScenes()->get(getState()->scene);
+ Position position = scene->position;
+
+ if (getState()->sceneUseBackup) {
+ Scene *sceneBackup = getScenes()->get(getState()->sceneBackup);
+ position = sceneBackup->position;
+ }
+
+ // Checks are different for each item
+ if ((item == kItem3 && position == 56)
+ || (item == kItem5 && (position >= 23 && position <= 32))
+ || (item == kItem7 && (position == 1 || (position >= 22 && position <= 33)))) {
+ if (getState()->sceneUseBackup)
+ getState()->sceneBackup = getSceneIndexFromPosition(car, position);
+ else
+ loadSceneFromPosition(car, position);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene drawing & processing
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::setScene(SceneIndex index) {
+ _flagNoEntity = false;
+
+ if (_flagDrawEntities) {
+ // TODO Setup screen size (0, 80)x(480x480) (is it necessary for our animations?)
+ drawScene(index);
+ _flagNoEntity = true;
+ } else {
+ _flagDrawEntities = true;
+ drawScene(index);
+ _flagDrawEntities = false;
+ }
+}
+
+void SceneManager::drawScene(SceneIndex index) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Preprocess
+ preProcessScene(&index);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw background
+ debugC(9, kLastExpressDebugScenes, "== Drawing scene: %d ==", index);
+
+ // Update scene
+ _engine->getGraphicsManager()->draw(get(index), GraphicsManager::kBackgroundC, true);
+ getState()->scene = index;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update entities
+ Scene *scene = (getState()->sceneUseBackup ? get(getState()->sceneBackup) : get(index));
+
+ getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition;
+ getEntityData(kEntityPlayer)->car = scene->car;
+
+ getFlags()->flag_3 = true;
+
+ if (getFlags()->isGameRunning) {
+ getSavePoints()->pushAll(kEntityPlayer, kActionDrawScene);
+ getSavePoints()->process();
+
+ if (_flagNoEntity)
+ return;
+
+ getEntities()->updateFields();
+ getEntities()->updateSequences();
+ getEntities()->updateCallbacks();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Show the scene
+ askForRedraw();
+ redrawScreen();
+
+ ////////////////////////////////////////////////////////////
+ // Post process scene
+ postProcessScene();
+}
+
+void SceneManager::processScene() {
+ if (!getState()->sceneUseBackup) {
+ loadScene(getState()->scene);
+ return;
+ }
+
+ getState()->sceneUseBackup = false;
+
+ // Select item if needed
+ InventoryItem item = getInventory()->getFirstExaminableItem();
+ if (item && getInventory()->getSelectedItem() == item)
+ getInventory()->selectItem(item);
+
+ Scene *backup = getScenes()->get(getState()->sceneBackup);
+
+ if (getEntities()->getPosition(backup->car, backup->position))
+ loadScene(processIndex(getState()->sceneBackup));
+ else
+ loadScene(getState()->sceneBackup);
+}
+
+LastExpress::SceneIndex SceneManager::processIndex(SceneIndex index) {
+ Scene *scene = get(index);
+ CarIndex car = scene->car;
+
+ switch (car) {
+ default:
+ break;
+
+ case kCarRedSleeping:
+ if (checkPosition(index, kCheckPositionLookingAtDoors)) {
+ Position position = (Position)(scene->position + (checkPosition(kSceneNone, kCheckPositionLookingUp) ? -1 : 1));
+
+ if (position == 4)
+ position = 3;
+
+ if (position == 24)
+ position = 25;
+
+ if (getEntities()->getPosition(car, position))
+ return index;
+ else
+ return getSceneIndexFromPosition(car, position);
+ } else {
+ switch (scene->position) {
+ default:
+ break;
+
+ case 41:
+ case 51:
+ if (!getEntities()->getPosition(car, 39))
+ return getSceneIndexFromPosition(car, 39);
+ // Fallback to next case
+
+ case 42:
+ case 52:
+ if (!getEntities()->getPosition(car, 14))
+ return getSceneIndexFromPosition(car, 14);
+ // Fallback to next case
+
+ case 43:
+ case 53:
+ if (!getEntities()->getPosition(car, 35))
+ return getSceneIndexFromPosition(car, 35);
+ // Fallback to next case
+
+ case 44:
+ case 54:
+ if (!getEntities()->getPosition(car, 10))
+ return getSceneIndexFromPosition(car, 10);
+ // Fallback to next case
+
+ case 45:
+ case 55:
+ if (!getEntities()->getPosition(car, 32))
+ return getSceneIndexFromPosition(car, 32);
+ // Fallback to next case
+
+ case 46:
+ case 56:
+ if (!getEntities()->getPosition(car, 7))
+ return getSceneIndexFromPosition(car, 7);
+ // Fallback to next case
+
+ case 47:
+ case 57:
+ if (!getEntities()->getPosition(car, 27))
+ return getSceneIndexFromPosition(car, 27);
+ // Fallback to next case
+
+ case 48:
+ case 58:
+ if (!getEntities()->getPosition(car, 2))
+ return getSceneIndexFromPosition(car, 2);
+ break;
+ }
+ }
+ break;
+
+ case kCarRestaurant:
+ switch (scene->position) {
+ default:
+ break;
+
+ case 52:
+ case 53:
+ case 54:
+ if (!getEntities()->getPosition(car, 51))
+ return getSceneIndexFromPosition(car, 51);
+ // Fallback to next case
+
+ case 50:
+ case 56:
+ case 57:
+ case 58:
+ if (!getEntities()->getPosition(car, 55))
+ return getSceneIndexFromPosition(car, 55);
+ // Fallback to next case
+
+ case 59:
+ if (!getEntities()->getPosition(car, 60))
+ return getSceneIndexFromPosition(car, 60);
+ // Fallback to next case
+
+ case 60:
+ if (!getEntities()->getPosition(car, 59))
+ return getSceneIndexFromPosition(car, 59);
+ // Fallback to next case
+
+ case 62:
+ case 63:
+ case 64:
+ if (!getEntities()->getPosition(car, 61))
+ return getSceneIndexFromPosition(car, 61);
+ // Fallback to next case
+
+ case 66:
+ case 67:
+ case 68:
+ if (!getEntities()->getPosition(car, 65))
+ return getSceneIndexFromPosition(car, 65);
+ // Fallback to next case
+
+ case 69:
+ case 71:
+ if (!getEntities()->getPosition(car, 70))
+ return getSceneIndexFromPosition(car, 70);
+ break;
+ }
+ break;
+ }
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+bool SceneManager::checkPosition(SceneIndex index, CheckPositionType type) const {
+ Scene *scene = getScenes()->get((index ? index : getState()->scene));
+
+ CarIndex car = (CarIndex)scene->car;
+ Position position = scene->position;
+
+ bool isInSleepingCar = (car == kCarGreenSleeping || car == kCarRedSleeping);
+
+ switch (type) {
+ default:
+ error("SceneManager::checkPosition: Invalid position type: %d", type);
+
+ case kCheckPositionLookingUp:
+ return isInSleepingCar && (position >= 1 && position <= 19);
+
+ case kCheckPositionLookingDown:
+ return isInSleepingCar && (position >= 21 && position <= 40);
+
+ case kCheckPositionLookingAtDoors:
+ return isInSleepingCar && ((position >= 2 && position <= 17) || (position >= 23 && position <= 39));
+
+ case kCheckPositionLookingAtClock:
+ return car == kCarRestaurant && position == 81;
+ }
+}
+
+bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ Position position = scene->position;
+ CarIndex car = (CarIndex)scene->car;
+
+ if (!doCheckOtherCars)
+ return (car == kCarGreenSleeping || car == kCarRedSleeping)
+ && ((position >= 41 && position <= 48) || (position >= 51 && position <= 58));
+
+ if (position == 99)
+ return true;
+
+ switch (car){
+ default:
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ case kCarLocomotive:
+ if ((position >= 1 && position <= 18) || (position >= 22 && position <= 40))
+ return true;
+ break;
+
+ case kCarRestaurant:
+ if (position >= 73 && position <= 80)
+ return true;
+
+ if (position == 10 || position == 11)
+ return true;
+
+ break;
+
+ case kCarBaggage:
+ switch (position) {
+ default:
+ break;
+
+ case 10:
+ case 11:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ case 84:
+ case 90:
+ case 91:
+ return true;
+ }
+ break;
+
+ case kCarCoalTender:
+ if (position == 2 || position == 10 || position == 11)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Train
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::updateDoorsAndClock() {
+ // Clear all sequences from the list
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door) {
+ removeFromQueue(*door);
+ setCoordinates(*door);
+ SAFE_DELETE(*door);
+ }
+
+ // Cleanup doors sequences
+ _doors.clear();
+
+ if (_clockHours) {
+ removeFromQueue(_clockHours);
+ setCoordinates(_clockHours);
+ SAFE_DELETE(_clockHours);
+ }
+
+ if (_clockMinutes) {
+ removeFromQueue(_clockMinutes);
+ setCoordinates(_clockMinutes);
+ SAFE_DELETE(_clockMinutes);
+ }
+
+ // Queue doors sequences for display
+ if (checkPosition(kSceneNone, kCheckPositionLookingAtDoors)) {
+
+ ObjectIndex firstIndex = kObjectNone;
+
+ // Init objectIndex (or exit if not in one of the two compartment cars
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping)
+ firstIndex = kObjectCompartment1;
+ else if (getEntityData(kEntityPlayer)->car == kCarRedSleeping)
+ firstIndex = kObjectCompartmentA;
+ else
+ return;
+
+ // Iterate over each door
+ for (ObjectIndex index = firstIndex; index < (ObjectIndex)(firstIndex + 8); index = (ObjectIndex)(index + 1)) {
+
+ // Doors is not open, nothing to do
+ if (getObjects()->get(index).location != kObjectLocation2)
+ continue;
+
+ // Load door sequence
+ Scene *scene = getScenes()->get(getState()->scene);
+ Common::String name = Common::String::format("633X%c-%02d.seq", (index - firstIndex) + 65, scene->position);
+ Sequence *sequence = loadSequence1(name, 255);
+
+ // If the sequence doesn't exists, skip
+ if (!sequence || !sequence->isLoaded())
+ continue;
+
+ // Adjust frame data and store in frame list
+ SequenceFrame *frame = new SequenceFrame(sequence, 0, true);
+ frame->getInfo()->location = (checkPosition(kSceneNone, kCheckPositionLookingUp) ? (firstIndex - index) - 1 : (index - firstIndex) - 8);
+
+ _doors.push_back(frame);
+
+ // Add frame to list
+ addToQueue(frame);
+ }
+ }
+
+ // Queue clock sequences for display
+ if (checkPosition(kSceneNone, kCheckPositionLookingAtClock)) {
+ // Only used in scene 349 to show the hands on the clock
+
+ Sequence *sequenceHours = loadSequence1("SCLKH-81.seq", 255);
+ Sequence *sequenceMinutes = loadSequence1("SCLKM-81.seq", 255);
+
+ // Compute hours and minutes indexes
+ uint16 hoursIndex = (uint)getState()->time % 1296000 % 54000 / 900;
+
+ uint hours = ((uint)getState()->time % 1296000) / 54000;
+ if (hours >= 12)
+ hours -= 12;
+
+ uint16 minutesIndex = (uint16)(5 * hours + hoursIndex / 12);
+
+ // Adjust z-order and store sequences to list
+ _clockHours = new SequenceFrame(sequenceHours, hoursIndex, true);
+ _clockHours->getInfo()->location = 65534;
+
+ _clockMinutes = new SequenceFrame(sequenceMinutes, minutesIndex, true);
+ _clockMinutes->getInfo()->location = 65535;
+
+ addToQueue(_clockHours);
+ addToQueue(_clockMinutes);
+ }
+}
+
+void SceneManager::resetDoorsAndClock() {
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door)
+ SAFE_DELETE(*door);
+
+ _doors.clear();
+
+ SAFE_DELETE(_clockHours);
+ SAFE_DELETE(_clockMinutes);
+
+ // Remove the beetle sequences too if needed
+ getBeetle()->unload();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sequence list
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::drawFrames(bool refreshScreen) {
+ if (!_flagDrawSequences)
+ return;
+
+ // TODO handle flag coordinates
+
+ clearBg(GraphicsManager::kBackgroundOverlay);
+
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i)
+ _engine->getGraphicsManager()->draw(*i, GraphicsManager::kBackgroundOverlay);
+
+ if (refreshScreen) {
+ askForRedraw();
+ //redrawScreen();
+
+ _flagDrawSequences = false;
+ }
+}
+
+void SceneManager::addToQueue(SequenceFrame * const frame) {
+ if (!frame)
+ return;
+
+ // First check that the frame is not already in the queue
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->equal(*i))
+ return;
+ }
+
+ debugC(8, kLastExpressDebugGraphics, "Adding frame: %s / %d", frame->getName().c_str(), frame->getFrame());
+
+ // Set flag
+ _flagDrawSequences = true;
+
+ // Queue empty: just insert the frame
+ if (_queue.empty()) {
+ _queue.push_back(frame);
+ return;
+ }
+
+ // Frame is closer: insert in first place
+ if (frame->getInfo()->location > _queue.front()->getInfo()->location) {
+ _queue.push_front(frame);
+ return;
+ }
+
+ // Insert the frame in the queue based on location
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->getInfo()->location > (*i)->getInfo()->location) {
+ _queue.insert(i, frame);
+ return;
+ }
+ }
+
+ // We are the last frame in location order, insert at the back of the queue
+ _queue.push_back(frame);
+}
+
+void SceneManager::removeFromQueue(SequenceFrame *frame) {
+ if (!frame)
+ return;
+
+ debugC(8, kLastExpressDebugGraphics, "Removing frame: %s / %d", frame->getName().c_str(), frame->getFrame());
+
+ // Check that the frame is in the queue and remove it
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->equal(*i)) {
+ _queue.erase(i);
+ _flagDrawSequences = true;
+ break;
+ }
+ }
+}
+
+void SceneManager::removeAndRedraw(SequenceFrame **frame, bool doRedraw) {
+ if (!frame)
+ return;
+
+ removeFromQueue(*frame);
+
+ if (doRedraw)
+ drawFrames(true);
+
+ SAFE_DELETE(*frame);
+}
+
+void SceneManager::resetQueue() {
+ _flagDrawSequences = true;
+
+ // The original engine only deletes decompressed data, not the "sequences" since they are just pointers to a memory pool
+ _queue.clear();
+}
+
+void SceneManager::setCoordinates(SequenceFrame *frame) {
+
+ if (!frame || frame->getInfo()->subType == 3)
+ return;
+
+ _flagCoordinates = true;
+
+ if (_coords.right > (int)frame->getInfo()->xPos1)
+ _coords.right = (int16)frame->getInfo()->xPos1;
+
+ if (_coords.bottom > (int)frame->getInfo()->yPos1)
+ _coords.bottom = (int16)frame->getInfo()->yPos1;
+
+ if (_coords.left < (int)frame->getInfo()->xPos2)
+ _coords.left = (int16)frame->getInfo()->xPos2;
+
+ if (_coords.top < (int)frame->getInfo()->yPos2)
+ _coords.top = (int16)frame->getInfo()->yPos2;
+}
+
+void SceneManager::resetCoordinates() {
+ _coords.top = 0;
+ _coords.left = 0;
+ _coords.bottom = 480;
+ _coords.right = 640;
+
+ _flagCoordinates = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////////
+SceneIndex SceneManager::getSceneIndexFromPosition(CarIndex car, Position position, int param3) {
+ // Probably can't happen (can we be called during cd-swap?)
+ if (_sceneLoader->count() <= 1)
+ return getState()->scene;
+
+ SceneIndex index = kSceneMenu;
+
+ Scene *firstScene = getScenes()->get(index);
+
+ while (firstScene->car != car
+ || firstScene->position != position
+ || ((param3 != -1 || firstScene->param3) && firstScene->param3 != param3 && firstScene->type != Scene::kTypeItem3)) {
+
+ // Increment index and look at the next scene
+ index = (SceneIndex)(index + 1);
+
+ if (index >= _sceneLoader->count())
+ return getState()->scene;
+
+ // Load the next scene
+ firstScene = getScenes()->get(index);
+ }
+
+ // Process index if necessary
+ Scene *scene = getScenes()->get(index);
+ if (getEntities()->getPosition(scene->car, scene->position))
+ return processIndex(index);
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene processing
+//////////////////////////////////////////////////////////////////////////
+
+// Process hotspots
+// - if it returns kSceneInvalid, the hotspot scene has not been modified
+// - if it returns kSceneNone, it has been modified
+//
+// Note: we use the original hotspot scene to pre-process again
+#define PROCESS_HOTSPOT_SCENE(hotspot, index) { \
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot); \
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? (hotspot)->scene : processedScene; \
+ if (testScene) { \
+ *index = (hotspot)->scene; \
+ preProcessScene(index); \
+ } \
+}
+
+void SceneManager::preProcessScene(SceneIndex *index) {
+
+ // Check index validity
+ if (*index == 0 || *index > 2500)
+ *index = kSceneMenu;
+
+ Scene *scene = getScenes()->get(*index);
+
+ switch (scene->type) {
+ case Scene::kTypeObject: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+
+ if (object >= kObjectMax)
+ break;
+
+ if (getObjects()->get(object).location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getObjects()->get(object).location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem: {
+ InventoryItem item = (InventoryItem)scene->param1;
+
+ if (item >= kPortraitOriginal)
+ break;
+
+ if (getInventory()->get(item)->location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getInventory()->get(item)->location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem2: {
+ InventoryItem item1 = (InventoryItem)scene->param1;
+ InventoryItem item2 = (InventoryItem)scene->param2;
+
+ if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getInventory()->get(item1)->location != kObjectLocationNone)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item2)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getInventory()->get(item1)->location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item2)->location != (*it)->param2)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeObjectItem: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+ InventoryItem item = (InventoryItem)scene->param2;
+
+ if (object >= kObjectMax)
+ break;
+
+ if (item >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getObjects()->get(object).location == kObjectLocation2)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getObjects()->get(object).location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item)->location != (*it)->param2)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem3: {
+ InventoryItem item1 = (InventoryItem)scene->param1;
+ InventoryItem item2 = (InventoryItem)scene->param2;
+ InventoryItem item3 = (InventoryItem)scene->param3;
+
+ if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal || item3 >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getInventory()->get(item1)->location != kObjectLocationNone)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item2)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (getInventory()->get(item3)->location != kObjectLocationNone)
+ location |= kObjectLocation4;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getInventory()->get(item1)->location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item2)->location != (*it)->param2)
+ continue;
+
+ if (getInventory()->get(item3)->location != (*it)->param3)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeObjectLocation2: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+
+ if (object >= kObjectMax)
+ break;
+
+ bool found = false;
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getObjects()->get(object).location2 != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ found = true;
+ break;
+ }
+
+ // If we haven't found a proper hotspot, use the first hotspot from the current scene
+ if (!found) {
+ Scene *sceneHotspot = getScenes()->get(*index);
+ SceneHotspot *hotspot = sceneHotspot->getHotspot();
+
+ PROCESS_HOTSPOT_SCENE(hotspot, index);
+ }
+ break;
+ }
+
+ case Scene::kTypeCompartments:
+ case Scene::kTypeCompartmentsItem:
+ if (scene->param1 >= 16)
+ break;
+
+ if (getEntities()->getCompartments(scene->param1) || getEntities()->getCompartments1(scene->param1)) {
+
+ Scene *currentScene = getScenes()->get(getState()->scene);
+
+ if ((checkPosition(getState()->scene, kCheckPositionLookingUp) && checkPosition(*index, kCheckPositionLookingUp) && currentScene->entityPosition < scene->entityPosition)
+ || (checkPosition(getState()->scene, kCheckPositionLookingDown) && checkPosition(*index, kCheckPositionLookingDown) && currentScene->entityPosition > scene->entityPosition)) {
+
+ if (State::getPowerOfTwo((uint32)getEntities()->getCompartments(scene->param1)) != 30
+ && State::getPowerOfTwo((uint32)getEntities()->getCompartments1(scene->param1)) != 30 )
+ getSound()->playSound(kEntityPlayer, "CAT1126A");
+
+ *index = scene->getHotspot()->scene;
+ } else {
+ *index = scene->getHotspot(1)->scene;
+ }
+
+ preProcessScene(index);
+ } else {
+ // Stop processing here for kTypeCompartments
+ if (scene->type == Scene::kTypeCompartments)
+ break;
+
+ InventoryItem item = (InventoryItem)scene->param2;
+ if (item >= kPortraitOriginal)
+ break;
+
+ if (getInventory()->get(item)->location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getInventory()->get(item)->location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Sound processing
+ Scene *newScene = getScenes()->get(*index);
+ if (getSound()->isBuffered(kEntityTables4)) {
+ if (newScene->type != Scene::kTypeReadText || newScene->param1)
+ getSound()->processEntry(kEntityTables4);
+ }
+
+ // Cleanup beetle sequences
+ if (getBeetle()->isLoaded()) {
+ if (newScene->type != Scene::kTypeLoadBeetleSequences)
+ getBeetle()->unload();
+ }
+}
+
+void SceneManager::postProcessScene() {
+
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ switch (scene->type) {
+ case Scene::kTypeList: {
+
+ // Adjust time
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)((scene->param1 + 10) * getState()->timeDelta));
+ getState()->timeTicks += (scene->param1 + 10);
+
+ // Wait for a number of frames unless right mouse is clicked
+ uint32 nextFrameCount = getFrameCount() + 4 * scene->param1;
+ if (!getFlags()->mouseRightClick) {
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ getSound()->updateSubtitles();
+ }
+ }
+
+ // Process hotspots and load scenes in the list
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (getFlags()->mouseRightClick) {
+
+ while (getScenes()->get(testScene)->type == Scene::kTypeList) {
+ hotspot = getScenes()->get(testScene)->getHotspot();
+ processedScene = getAction()->processHotspot(*hotspot);
+ testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+ }
+ }
+
+ // If several entities are there, choose one to sound "Excuse me"
+ EntityPosition entityPosition = getEntityData(kEntityPlayer)->entityPosition;
+ if (getEntityData(kEntityPlayer)->car == kCar9 && (entityPosition == kPosition_4 || entityPosition == kPosition_3)) {
+ EntityIndex entities[39];
+
+ // Init entities
+ entities[0] = kEntityPlayer;
+
+ uint progress = 0;
+
+ for (uint i = 1; i < 40 /* number of entities */; i++) {
+ CarIndex car = getEntityData((EntityIndex)i)->car;
+ EntityPosition position = getEntityData((EntityIndex)i)->entityPosition;
+
+ if (entityPosition == kPosition_4) {
+ if ((car == kCarRedSleeping && position > kPosition_9270) || (car == kCarRestaurant && position < kPosition_1540))
+ entities[progress++] = (EntityIndex)i;
+ } else {
+ if ((car == kCarGreenSleeping && position > kPosition_9270) || (car == kCarRedSleeping && position < kPosition_850))
+ entities[progress++] = (EntityIndex)i;
+ }
+ }
+
+ if (progress)
+ getSound()->excuseMe((progress == 1) ? entities[0] : entities[rnd(progress)], kEntityPlayer, SoundManager::kFlagDefault);
+ }
+
+ if (hotspot->scene)
+ setScene(hotspot->scene);
+ break;
+ }
+
+ case Scene::kTypeSavePointChapter:
+ if (getProgress().field_18 == 2)
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionEndChapter);
+ break;
+
+ case Scene::kTypeLoadBeetleSequences:
+ if ((getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3)
+ && getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ if (!getBeetle()->isLoaded())
+ getBeetle()->load();
+ }
+ break;
+
+ case Scene::kTypeGameOver:
+ if (getState()->time >= kTimeCityGalanta || getProgress().field_18 == 4)
+ break;
+
+ getSound()->processEntry(SoundManager::kSoundType7);
+ getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault);
+
+ switch (getProgress().chapter) {
+ default:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverPolice2, true);
+ break;
+
+ case kChapter1:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm, true);
+ break;
+
+ case kChapter4:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm2, true);
+ break;
+ }
+ break;
+
+ case Scene::kTypeReadText:
+ getSound()->readText(scene->param1);
+ break;
+
+ case Scene::kType133:
+ if (getFlags()->flag_0) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ getLogic()->updateCursor();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h
new file mode 100644
index 0000000000..b70526839f
--- /dev/null
+++ b/engines/lastexpress/game/scenes.h
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SCENEMANAGER_H
+#define LASTEXPRESS_SCENEMANAGER_H
+
+#include "lastexpress/data/scene.h"
+
+#include "common/hashmap.h"
+#include "common/list.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class SceneLoader;
+class Sequence;
+class SequenceFrame;
+
+class SceneManager {
+public:
+ enum CheckPositionType {
+ kCheckPositionLookingUp,
+ kCheckPositionLookingDown,
+ kCheckPositionLookingAtDoors,
+ kCheckPositionLookingAtClock
+ };
+
+ SceneManager(LastExpressEngine *engine);
+ ~SceneManager();
+
+ // Scene cache
+ void loadSceneDataFile(ArchiveIndex archive);
+ Scene *get(SceneIndex sceneIndex) { return _sceneLoader->get(sceneIndex); }
+
+ // Scene loading
+ void setScene(SceneIndex sceneIndex);
+ void loadScene(SceneIndex sceneIndex);
+ void loadSceneFromObject(ObjectIndex object, bool alternate = false);
+ void loadSceneFromItem(InventoryItem item);
+ void loadSceneFromItemPosition(InventoryItem item);
+ void loadSceneFromPosition(CarIndex car, Position position, int param3 = -1);
+
+ // Scene drawing & processing
+ void drawScene(SceneIndex sceneIndex);
+ void processScene();
+ SceneIndex processIndex(SceneIndex sceneIndex);
+
+ // Checks
+ bool checkPosition(SceneIndex sceneIndex, CheckPositionType type) const;
+ bool checkCurrentPosition(bool doCheckOtherCars) const;
+
+ // Train
+ void updateDoorsAndClock();
+ void resetDoorsAndClock();
+
+ // Sequence queue
+ void drawFrames(bool refreshScreen);
+ void addToQueue(SequenceFrame * const frame);
+ void removeFromQueue(SequenceFrame *frame);
+ void removeAndRedraw(SequenceFrame **frame, bool doRedraw);
+ void resetQueue();
+ void setCoordinates(SequenceFrame *frame);
+
+ // Helpers
+ SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1);
+
+ void setFlagDrawSequences() { _flagDrawSequences = true; }
+
+private:
+ LastExpressEngine *_engine;
+ SceneLoader *_sceneLoader; ///< Scene loader
+
+ // Flags
+ bool _flagNoEntity;
+ bool _flagDrawEntities;
+ bool _flagDrawSequences;
+ bool _flagCoordinates;
+
+ Common::Rect _coords;
+
+ // Train sequences
+ Common::List<SequenceFrame *> _doors;
+ SequenceFrame *_clockHours;
+ SequenceFrame *_clockMinutes;
+
+ // Sequence queue
+ Common::List<SequenceFrame *> _queue;
+
+ // Scene processing
+ void preProcessScene(SceneIndex *index);
+ void postProcessScene();
+
+ void resetCoordinates();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SCENEMANAGER_H
diff --git a/engines/lastexpress/game/sound.cpp b/engines/lastexpress/game/sound.cpp
new file mode 100644
index 0000000000..11c60a1b9b
--- /dev/null
+++ b/engines/lastexpress/game/sound.cpp
@@ -0,0 +1,1771 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+// Letters & messages
+const char *messages[24] = {
+ "",
+ "TXT1001", // 1
+ "TXT1001A", // 2
+ "TXT1011", // 3
+ "TXT1012", // 4
+ "TXT1013", // 5
+ "TXT1014", // 6
+ "TXT1020", // 7
+ "TXT1030", // 8
+ "END1009B", // 50
+ "END1046", // 51
+ "END1047", // 52
+ "END1112", // 53
+ "END1112A", // 54
+ "END1503", // 55
+ "END1505A", // 56
+ "END1505B", // 57
+ "END1610", // 58
+ "END1612A", // 59
+ "END1612C", // 61
+ "END1612D", // 62
+ "ENDALRM1", // 63
+ "ENDALRM2", // 64
+ "ENDALRM3" // 65
+};
+
+const char *cities[17] = {
+ "EPERNAY",
+ "CHALONS",
+ "BARLEDUC",
+ "NANCY",
+ "LUNEVILL",
+ "AVRICOUR",
+ "DEUTSCHA",
+ "STRASBOU",
+ "BADENOOS",
+ "SALZBURG",
+ "ATTNANG",
+ "WELS",
+ "LINZ",
+ "VIENNA",
+ "POZSONY",
+ "GALANTA",
+ "POLICE"
+};
+
+const char *locomotiveSounds[5] = {
+ "ZFX1005",
+ "ZFX1006",
+ "ZFX1007",
+ "ZFX1007A",
+ "ZFX1007B"
+};
+
+static const SoundManager::FlagType soundFlags[32] = {
+ SoundManager::kFlagDefault, SoundManager::kFlag15, SoundManager::kFlag14, SoundManager::kFlag13, SoundManager::kFlag12,
+ SoundManager::kFlag11, SoundManager::kFlag11, SoundManager::kFlag10, SoundManager::kFlag10, SoundManager::kFlag9, SoundManager::kFlag9, SoundManager::kFlag8, SoundManager::kFlag8,
+ SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag6, SoundManager::kFlag6, SoundManager::kFlag6,
+ SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4,
+ SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3
+};
+
+SoundManager::SoundManager(LastExpressEngine *engine) : _engine(engine), _state(0), _currentType(kSoundType16), _flag(0) {
+ _soundStream = new StreamedSound();
+
+ // Initialize unknown data
+ _data0 = 0;
+ _data1 = 0;
+ _data2 = 0;
+
+ memset(&_buffer, 0, sizeof(_buffer));
+ memset(&_lastWarning, 0, sizeof(_lastWarning));
+}
+
+SoundManager::~SoundManager() {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
+ SAFE_DELETE(*i);
+
+ _cache.clear();
+
+ SAFE_DELETE(_soundStream);
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Timer
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::handleTimer() {
+ Common::StackLock locker(_mutex);
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = (*i);
+ if (entry->stream == NULL) {
+ SAFE_DELETE(*i);
+ i = _cache.reverse_erase(i);
+ continue;
+ } else if (!entry->isStreamed) {
+ entry->isStreamed = true;
+
+ // TODO: stream any sound in the queue after filtering
+ _soundStream->load(entry->stream);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound queue management
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::updateQueue() {
+ // TODO add mutex lock!
+ //warning("Sound::unknownFunction1: not implemented!");
+}
+
+void SoundManager::resetQueue(SoundType type1, SoundType type2) {
+ if (!type2)
+ type2 = type1;
+
+ Common::StackLock locker(_mutex);
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->type != type1 && (*i)->type != type2)
+ resetEntry(*i);
+ }
+}
+
+void SoundManager::removeFromQueue(EntityIndex entity) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(entity);
+ if (entry)
+ resetEntry(entry);
+}
+
+void SoundManager::removeFromQueue(Common::String filename) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(filename);
+ if (entry)
+ resetEntry(entry);
+}
+
+void SoundManager::clearQueue() {
+ _flag |= 4;
+
+ // FIXME: Wait a while for a flag to be set
+ //for (int i = 0; i < 3000000; i++)
+ // if (_flag & 8)
+ // break;
+
+ _flag |= 8;
+
+ Common::StackLock locker(_mutex);
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = (*i);
+
+ // Delete entry
+ removeEntry(entry);
+ SAFE_DELETE(entry);
+
+ i = _cache.reverse_erase(i);
+ }
+
+ updateSubtitles();
+}
+
+bool SoundManager::isBuffered(EntityIndex entity) {
+ Common::StackLock locker(_mutex);
+
+ return (getEntry(entity) != NULL);
+}
+
+bool SoundManager::isBuffered(Common::String filename, bool testForEntity) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(filename);
+
+ if (testForEntity)
+ return entry != NULL && !entry->entity;
+
+ return (entry != NULL);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entry
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4) {
+ if (!entry)
+ error("SoundManager::setupEntry: Invalid entry!");
+
+ entry->field_4C = a4;
+ setEntryType(entry, flag);
+ setEntryStatus(entry, flag);
+
+ // Add entry to cache
+ _cache.push_back(entry);
+
+ setupCache(entry);
+ loadSoundData(entry, name);
+}
+
+void SoundManager::setEntryType(SoundEntry *entry, FlagType flag) {
+ switch (flag & kFlagType7) {
+ default:
+ case kFlagNone:
+ entry->type = _currentType;
+ _currentType = (SoundType)(_currentType + 1);
+ break;
+
+ case kFlagType1_2: {
+ SoundEntry *previous2 = getEntry(kSoundType2);
+ if (previous2)
+ updateEntry(previous2, 0);
+
+ SoundEntry *previous = getEntry(kSoundType1);
+ if (previous) {
+ previous->type = kSoundType2;
+ updateEntry(previous, 0);
+ }
+
+ entry->type = kSoundType1;
+ }
+ break;
+
+ case kFlagType3: {
+ SoundEntry *previous = getEntry(kSoundType3);
+ if (previous) {
+ previous->type = kSoundType4;
+ updateEntry(previous, 0);
+ }
+
+ entry->type = kSoundType11;
+ }
+ break;
+
+ case kFlagType7: {
+ SoundEntry *previous = getEntry(kSoundType7);
+ if (previous)
+ previous->type = kSoundType8;
+
+ entry->type = kSoundType7;
+ }
+ break;
+
+ case kFlagType9: {
+ SoundEntry *previous = getEntry(kSoundType9);
+ if (previous)
+ previous->type = kSoundType10;
+
+ entry->type = kSoundType9;
+ }
+ break;
+
+ case kFlagType11: {
+ SoundEntry *previous = getEntry(kSoundType11);
+ if (previous)
+ previous->type = kSoundType14;
+
+ entry->type = kSoundType11;
+ }
+ break;
+
+ case kFlagType13: {
+ SoundEntry *previous = getEntry(kSoundType13);
+ if (previous)
+ previous->type = kSoundType14;
+
+ entry->type = kSoundType13;
+ }
+ break;
+ }
+}
+
+void SoundManager::setEntryStatus(SoundEntry *entry, FlagType flag) const {
+ SoundStatus status = (SoundStatus)flag;
+ if (!((status & 0xFF) & kSoundStatusClear1))
+ status = (SoundStatus)(status | kSoundStatusClear2);
+
+ if (((status & 0xFF00) >> 8) & kSoundStatusClear0)
+ entry->status.status = (uint32)status;
+ else
+ entry->status.status = (status | kSoundStatusClear4);
+}
+
+bool SoundManager::setupCache(SoundEntry *entry) {
+ warning("Sound::setupCache: not implemented!");
+ return true;
+}
+
+void SoundManager::clearStatus() {
+ Common::StackLock locker(_mutex);
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
+ (*i)->status.status |= kSoundStatusClear3;
+}
+
+void SoundManager::loadSoundData(SoundEntry *entry, Common::String name) {
+ entry->name2 = name;
+
+ // Load sound data
+ entry->stream = getArchive(name);
+
+ if (!entry->stream)
+ entry->stream = getArchive("DEFAULT.SND");
+
+ if (entry->stream) {
+ warning("Sound::loadSoundData: not implemented!");
+ } else {
+ entry->status.status = kSoundStatusRemoved;
+ }
+}
+
+void SoundManager::resetEntry(SoundEntry *entry) {
+ entry->status.status |= kSoundStatusRemoved;
+ entry->entity = kEntityPlayer;
+
+ if (entry->stream) {
+ if (!entry->isStreamed)
+ SAFE_DELETE(entry->stream);
+
+ entry->stream = NULL;
+ }
+}
+
+
+void SoundManager::removeEntry(SoundEntry *entry) {
+ entry->status.status |= kSoundStatusRemoved;
+
+ // Loop until ready
+ while (!(entry->status.status1 & 4) && !(_flag & 8) && (_flag & 1))
+ ; // empty loop body
+
+ // The original game remove the entry from the cache here,
+ // but since we are called from within an iterator loop
+ // we will remove the entry there
+ // removeFromCache(entry);
+
+ if (entry->subtitle) {
+ drawSubtitles(entry->subtitle);
+ SAFE_DELETE(entry->subtitle);
+ }
+
+ if (entry->entity) {
+ if (entry->entity == kEntitySteam)
+ playLoopingSound();
+ else if (entry->entity != kEntityTrain)
+ getSavePoints()->push(kEntityPlayer, entry->entity, kActionEndSound);
+ }
+}
+
+void SoundManager::updateEntry(SoundEntry *entry, uint value) const {
+ if (!(entry->status.status3 & 64)) {
+ int value2 = value;
+
+ entry->status.status |= kSoundStatus_100000;
+
+ if (value) {
+ if (_flag & 32) {
+ entry->field_40 = value;
+ value2 = value * 2 + 1;
+ }
+
+ entry->field_3C = value2;
+ } else {
+ entry->field_3C = 0;
+ entry->status.status |= kSoundStatus_40000000;
+ }
+ }
+}
+
+void SoundManager::updateEntryState(SoundEntry *entry) const {
+ if (_flag & 32) {
+ if (entry->type != kSoundType9 && entry->type != kSoundType7 && entry->type != kSoundType5) {
+ uint32 status = entry->status.status & kSoundStatusClear1;
+
+ entry->status.status &= kSoundStatusClearAll;
+
+ entry->field_40 = status;
+ entry->status.status |= status * 2 + 1;
+ }
+ }
+
+ entry->status.status |= kSoundStatus_20;
+}
+
+void SoundManager::processEntry(EntityIndex entity) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(entity);
+ if (entry) {
+ updateEntry(entry, 0);
+ entry->entity = kEntityPlayer;
+ }
+}
+
+void SoundManager::processEntry(SoundType type) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(type);
+ if (entry)
+ updateEntry(entry, 0);
+}
+
+void SoundManager::setupEntry(SoundType type, EntityIndex index) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(type);
+ if (entry)
+ entry->entity = index;
+}
+
+void SoundManager::processEntry(Common::String filename) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(filename);
+ if (entry) {
+ updateEntry(entry, 0);
+ entry->entity = kEntityPlayer;
+ }
+}
+
+void SoundManager::processEntries() {
+ _state = 0;
+
+ processEntry(kSoundType1);
+ processEntry(kSoundType2);
+}
+
+uint32 SoundManager::getEntryTime(EntityIndex index) {
+ Common::StackLock locker(_mutex);
+
+ SoundEntry *entry = getEntry(index);
+ if (entry)
+ return entry->time;
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+
+void SoundManager::unknownFunction4() {
+ // TODO: Add mutex ?
+ warning("Sound::unknownFunction4: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entry search
+//////////////////////////////////////////////////////////////////////////
+SoundManager::SoundEntry *SoundManager::getEntry(EntityIndex index) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->entity == index)
+ return *i;
+ }
+
+ return NULL;
+}
+
+SoundManager::SoundEntry *SoundManager::getEntry(Common::String name) {
+ if (!name.contains('.'))
+ name += ".SND";
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->name2 == name)
+ return *i;
+ }
+
+ return NULL;
+}
+
+SoundManager::SoundEntry *SoundManager::getEntry(SoundType type) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->type == type)
+ return *i;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(_state);
+ s.syncAsUint32LE(_currentType);
+
+ // Compute the number of entries to save
+ uint32 numEntries = count();
+ s.syncAsUint32LE(numEntries);
+
+ Common::StackLock locker(_mutex);
+
+ // Save or load each entry data
+ if (s.isSaving()) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = *i;
+ if (entry->name2.matchString("NISSND?") && (entry->status.status & kFlagType7) != kFlag3) {
+ s.syncAsUint32LE(entry->status.status); // status;
+ s.syncAsUint32LE(entry->type); // type;
+ s.syncAsUint32LE(entry->field_1C); // field_8;
+ s.syncAsUint32LE(entry->time); // time;
+ s.syncAsUint32LE(entry->field_34); // field_10;
+ s.syncAsUint32LE(entry->field_38); // field_14;
+ s.syncAsUint32LE(entry->entity); // entity;
+
+ uint32 field_1C = (uint32)entry->field_48 - _data2;
+ if (field_1C > kFlag8)
+ field_1C = 0;
+ s.syncAsUint32LE(field_1C); // field_1C;
+
+ s.syncAsUint32LE(entry->field_4C); // field_20;
+
+ char name1[16];
+ strcpy((char *)&name1, entry->name1.c_str());
+ s.syncBytes((byte *)&name1, 16);
+
+ char name2[16];
+ strcpy((char *)&name2, entry->name2.c_str());
+ s.syncBytes((byte *)&name2, 16);
+ }
+ }
+ } else {
+ warning("Sound::saveLoadWithSerializer: not implemented!");
+ s.skip(numEntries * 64);
+ }
+}
+
+
+// FIXME: We probably need another mutex here to protect during the whole savegame process
+// as we could have removed an entry between the time we check the count and the time we
+// save the entries
+uint32 SoundManager::count() {
+ Common::StackLock locker(_mutex);
+
+ uint32 numEntries = 0;
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
+ if ((*i)->name2.matchString("NISSND?"))
+ ++numEntries;
+
+ return numEntries;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game-related functions
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playSound(EntityIndex entity, Common::String filename, FlagType flag, byte a4) {
+ if (isBuffered(entity) && entity)
+ removeFromQueue(entity);
+
+ FlagType currentFlag = (flag == -1) ? getSoundFlag(entity) : (FlagType)(flag | 0x80000);
+
+ // Add .SND at the end of the filename if needed
+ if (!filename.contains('.'))
+ filename += ".SND";
+
+ if (!playSoundWithSubtitles(filename, currentFlag, entity, a4))
+ if (entity)
+ getSavePoints()->push(kEntityPlayer, entity, kActionEndSound);
+}
+
+bool SoundManager::playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4) {
+ SoundEntry *entry = new SoundEntry();
+
+ Common::StackLock locker(_mutex);
+
+ setupEntry(entry, filename, flag, 30);
+ entry->entity = entity;
+
+ if (a4) {
+ entry->field_48 = _data2 + 2 * a4;
+ entry->status.status |= kSoundStatus_8000;
+ } else {
+ // Get subtitles name
+ while (filename.size() > 4)
+ filename.deleteLastChar();
+
+ showSubtitles(entry, filename);
+ updateEntryState(entry);
+ }
+
+ return (entry->type != kSoundTypeNone);
+}
+
+void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) {
+ char filename[12];
+ int values[5];
+
+ if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
+ return;
+
+ if (getEntities()->isInSalon(entity) != getEntities()->isInSalon(kEntityPlayer))
+ return;
+
+ int _action = (int)action;
+ FlagType flag = getSoundFlag(entity);
+
+ switch (action) {
+ case 36: {
+ int _param3 = (flag <= 9) ? flag + 7 : 16;
+
+ if (_param3 > 7) {
+ _data0 = (uint)_param3;
+ _data1 = _data2 + 2 * a3;
+ }
+ break;
+ }
+
+ case 37:
+ _data0 = 7;
+ _data1 = _data2 + 2 * a3;
+ break;
+
+ case 150:
+ case 156:
+ case 162:
+ case 168:
+ case 188:
+ case 198:
+ _action += 1 + (int)rnd(5);
+ break;
+
+ case 174:
+ case 184:
+ case 194:
+ _action += 1 + (int)rnd(3);
+ break;
+
+ case 180:
+ _action += 1 + (int)rnd(4);
+ break;
+
+ case 246:
+ values[0] = 0;
+ values[1] = 104;
+ values[2] = 105;
+ values[3] = 106;
+ values[4] = 116;
+ _action = values[rnd(5)];
+ break;
+
+ case 247:
+ values[0] = 11;
+ values[1] = 123;
+ values[2] = 124;
+ _action = values[rnd(3)];
+ break;
+
+ case 248:
+ values[0] = 0;
+ values[1] = 103;
+ values[2] = 108;
+ values[3] = 109;
+ _action = values[rnd(4)];
+ break;
+
+ case 249:
+ values[0] = 0;
+ values[1] = 56;
+ values[2] = 112;
+ values[3] = 113;
+ _action = values[rnd(4)];
+ break;
+
+ case 250:
+ values[0] = 0;
+ values[1] = 107;
+ values[2] = 115;
+ values[3] = 117;
+ _action = values[rnd(4)];
+ break;
+
+ case 251:
+ values[0] = 0;
+ values[1] = 11;
+ values[2] = 56;
+ values[3] = 113;
+ _action = values[rnd(4)];
+ break;
+
+ case 252:
+ values[0] = 0;
+ values[1] = 6;
+ values[2] = 109;
+ values[3] = 121;
+ _action = values[rnd(4)];
+ break;
+
+ case 254:
+ values[0] = 0;
+ values[1] = 104;
+ values[2] = 120;
+ values[3] = 121;
+ _action = values[rnd(4)];
+ break;
+
+ case 255:
+ values[0] = 0;
+ values[1] = 106;
+ values[2] = 115;
+ _action = values[rnd(3)];
+ break;
+
+ default:
+ break;
+ }
+
+ if (_action) {
+ sprintf((char *)&filename, "LIB%03d.SND", _action);
+
+ if (flag)
+ playSoundWithSubtitles((char*)&filename, flag, kEntityPlayer, a3);
+ }
+}
+
+void SoundManager::playSteam(CityIndex index) {
+ if (index >= ARRAYSIZE(cities))
+ error("SoundManager::playSteam: invalid city index (was %d, max %d)", index, ARRAYSIZE(cities));
+
+ _state |= kSoundState2;
+
+ if (!getEntry(kSoundType1))
+ playSoundWithSubtitles("STEAM.SND", kFlagSteam, kEntitySteam);
+
+ // Get the new sound entry and show subtitles
+ SoundEntry *entry = getEntry(kSoundType1);
+ if (entry)
+ showSubtitles(entry, cities[index]);
+}
+
+void SoundManager::playFightSound(byte action, byte a4) {
+ int _action = (int)action;
+ char filename[12];
+ int values[5];
+
+ switch (action) {
+ default:
+ break;
+
+ case 174:
+ case 184:
+ case 194:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ _action = values[rnd(3)];
+ break;
+
+ case 180:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ values[3] = action + 4;
+ _action = values[rnd(4)];
+ break;
+
+ case 150:
+ case 156:
+ case 162:
+ case 168:
+ case 188:
+ case 198:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ values[3] = action + 4;
+ values[4] = action + 5;
+ _action = values[rnd(5)];
+ break;
+ }
+
+ if (_action) {
+ sprintf((char *)&filename, "LIB%03d.SND", _action);
+ playSound(kEntityTrain, (char*)&filename, kFlagDefault, a4);
+ }
+}
+
+void SoundManager::playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4) {
+ if (isBuffered(getDialogName(entityDialog)))
+ removeFromQueue(getDialogName(entityDialog));
+
+ playSound(entity, getDialogName(entityDialog), flag, a4);
+}
+
+void SoundManager::playLocomotiveSound() {
+ playSound(kEntityPlayer, locomotiveSounds[rnd(5)], (FlagType)(rnd(15) + 2));
+}
+
+const char *SoundManager::getDialogName(EntityIndex entity) const {
+ switch (entity) {
+ case kEntityAnna:
+ if (getEvent(kEventAnnaDialogGoToJerusalem))
+ return "XANN12";
+
+ if (getEvent(kEventLocomotiveRestartTrain))
+ return "XANN11";
+
+ if (getEvent(kEventAnnaBaggageTies) || getEvent(kEventAnnaBaggageTies2) || getEvent(kEventAnnaBaggageTies3) || getEvent(kEventAnnaBaggageTies4))
+ return "XANN10";
+
+ if (getEvent(kEventAnnaTired) || getEvent(kEventAnnaTiredKiss))
+ return "XANN9";
+
+ if (getEvent(kEventAnnaBaggageArgument))
+ return "XANN8";
+
+ if (getEvent(kEventKronosVisit))
+ return "XANN7";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XANN6A";
+
+ if (getEvent(kEventVassiliSeizure))
+ return "XANN6";
+
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ return "XANN5";
+
+ if (getProgress().field_60)
+ return "XANN4";
+
+ if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram))
+ return "XANN3";
+
+ if (getEvent(kEventDinerMindJoin))
+ return "XANN2";
+
+ if (getEvent(kEventGotALight) || getEvent(kEventGotALightD))
+ return "XANN1";
+
+ break;
+
+ case kEntityAugust:
+ if (getEvent(kEventAugustTalkCigar))
+ return "XAUG6";
+
+ if (getEvent(kEventAugustBringBriefcase))
+ return "XAUG5";
+
+ // Getting closer to Vienna...
+ if (getState()->time > kTime2200500 && !getEvent(kEventAugustMerchandise))
+ return "XAUG4A";
+
+ if (getEvent(kEventAugustMerchandise))
+ return "XAUG4";
+
+ if (getEvent(kEventDinerAugust) || getEvent(kEventDinerAugustAlexeiBackground) || getEvent(kEventMeetAugustTylerCompartment)
+ || getEvent(kEventMeetAugustTylerCompartmentBed) || getEvent(kEventMeetAugustHisCompartment) || getEvent(kEventMeetAugustHisCompartmentBed))
+ return "XAUG3";
+
+ if (getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ return "XAUG2";
+
+ if (getProgress().eventMertensAugustWaiting)
+ return "XAUG1";
+
+ break;
+
+ case kEntityTatiana:
+ if (getEvent(kEventTatianaTylerCompartment))
+ return "XTAT6";
+
+ if (getEvent(kEventTatianaCompartmentStealEgg))
+ return "XTAT5";
+
+ if (getEvent(kEventTatianaGivePoem))
+ return "XTAT3";
+
+ if (getProgress().field_64)
+ return "XTAT1";
+
+ break;
+
+ case kEntityVassili:
+ if (getEvent(kEventCathFreePassengers))
+ return "XVAS4";
+
+ if (getEvent(kEventVassiliCompartmentStealEgg))
+ return "XVAS3";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XVAS2";
+
+ if (getEvent(kEventVassiliSeizure))
+ return "XVAS1A";
+
+ if (getProgress().field_64)
+ return "XVAS1";
+
+ break;
+
+ case kEntityAlexei:
+ if (getProgress().field_88)
+ return "XALX6";
+
+ if (getProgress().field_8C)
+ return "XALX5";
+
+ if (getProgress().field_90)
+ return "XALX4A";
+
+ if (getProgress().field_68)
+ return "XALX4";
+
+ if (getEvent(kEventAlexeiSalonPoem))
+ return "XALX3";
+
+ if (getEvent(kEventAlexeiSalonVassili))
+ return "XALX2";
+
+ if (getEvent(kEventAlexeiDiner) || getEvent(kEventAlexeiDinerOriginalJacket))
+ return "XALX1";
+
+ break;
+
+ case kEntityAbbot:
+ if (getEvent(kEventAbbotDrinkDefuse))
+ return "XABB4";
+
+ if (getEvent(kEventAbbotInvitationDrink) || getEvent(kEventDefuseBomb))
+ return "XABB3";
+
+ if (getEvent(kEventAbbotWrongCompartment) || getEvent(kEventAbbotWrongCompartmentBed))
+ return "XABB2";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XABB1";
+
+ break;
+
+ case kEntityMilos:
+ if (getEvent(kEventLocomotiveMilosDay) || getEvent(kEventLocomotiveMilosNight))
+ return "XMIL5";
+
+ if (getEvent(kEventMilosCompartmentVisitTyler) && (getProgress().chapter == kChapter3 || getProgress().chapter == kChapter4))
+ return "XMIL4";
+
+ if (getEvent(kEventMilosCorridorThanks) || getProgress().chapter == kChapter5)
+ return "XMIL3";
+
+ if (getEvent(kEventMilosCompartmentVisitAugust))
+ return "XMIL2";
+
+ if (getEvent(kEventMilosTylerCompartmentDefeat))
+ return "XMIL1";
+
+ break;
+
+ case kEntityVesna:
+ if (getProgress().field_94)
+ return "XVES2";
+
+ if (getProgress().field_98)
+ return "XVES1";
+
+ break;
+
+ case kEntityKronos:
+ if (getEvent(kEventKronosReturnBriefcase))
+ return "XKRO6";
+
+ if (getEvent(kEventKronosBringEggCeiling) || getEvent(kEventKronosBringEgg))
+ return "XKRO5";
+
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+ if (location != kObjectLocation6 && location != kObjectLocation5 && location != kObjectLocation2 && location != kObjectLocation1)
+ return "XKRO4A";
+ }
+
+ if (getEvent(kEventKronosConversationFirebird))
+ return "XKRO4";
+
+ if (getEvent(kEventKronosConversation)) {
+ if (!getEvent(kEventMilosCompartmentVisitAugust))
+ return "XKRO3";
+ else
+ return "XKRO2";
+ }
+
+ if (getProgress().eventMertensKronosInvitation)
+ return "XKRO1";
+
+ break;
+
+ case kEntityFrancois:
+ if (getProgress().field_9C)
+ return "XFRA3";
+
+ if (getProgress().field_A0
+ || getEvent(kEventFrancoisWhistle) || getEvent(kEventFrancoisWhistleD)
+ || getEvent(kEventFrancoisWhistleNight) || getEvent(kEventFrancoisWhistleNightD))
+ return "XFRA2";
+
+ if (getState()->time > kTimeParisEpernay) // Between Paris and Epernay
+ return "XFRA1";
+
+ break;
+
+ case kEntityMmeBoutarel:
+ if (getProgress().field_A4)
+ return "XMME4";
+
+ if (getProgress().field_A8)
+ return "XMME3";
+
+ if (getProgress().field_A0)
+ return "XMME2";
+
+ if (getProgress().field_AC)
+ return "XMME1";
+
+ break;
+
+ case kEntityBoutarel:
+ if (getProgress().eventMetBoutarel)
+ return "XMRB1";
+
+ break;
+
+ case kEntityRebecca:
+ if (getProgress().field_B4)
+ return "XREB1A";
+
+ if (getProgress().field_B8)
+ return "XREB1";
+
+ break;
+
+ case kEntitySophie:
+ if (getProgress().field_B0)
+ return "XSOP2";
+
+ if (getProgress().field_BC)
+ return "XSOP1B";
+
+ if (getProgress().field_B4)
+ return "XSOP1A";
+
+ if (getProgress().field_B8)
+ return "XSOP1";
+
+ break;
+
+ case kEntityMahmud:
+ if (getProgress().field_C4)
+ return "XMAH1";
+
+ break;
+
+ case kEntityYasmin:
+ if (getProgress().eventMetYasmin)
+ return "XHAR2";
+
+ break;
+
+ case kEntityHadija:
+ if (getProgress().eventMetHadija)
+ return "XHAR1";
+
+ break;
+
+ case kEntityAlouan:
+ if (getProgress().field_DC)
+ return "XHAR3";
+
+ break;
+
+ case kEntityGendarmes:
+ if (getProgress().field_E0)
+ return "XHAR4";
+
+ break;
+
+ case kEntityChapters:
+ if (getEvent(kEventCathDream) || getEvent(kEventCathWakingUp))
+ return "XTYL3";
+
+ return "XTYL1";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Letters & Messages
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::readText(int id){
+ if (!isBuffered(kEntityTables4))
+ return;
+
+ if (id < 0 || (id > 8 && id < 50) || id > 64)
+ error("Sound::readText - attempting to use invalid id. Valid values [1;8] - [50;64], was %d", id);
+
+ // Get proper message file (names are stored in sequence in the array but id is [1;8] - [50;64])
+ const char *text = messages[id <= 8 ? id : id - 41];
+
+ // Check if file is in cache for id [1;8]
+ if (id <= 8)
+ if (isBuffered(text))
+ removeFromQueue(text);
+
+ playSound(kEntityTables4, text, kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound bites
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playWarningCompartment(EntityIndex entity, ObjectIndex compartment) {
+
+#define PLAY_WARNING(index, sound1, sound2, sound3, sound4, sound5, sound6) { \
+ if (_lastWarning[index] + 450 >= getState()->timeTicks) { \
+ if (rnd(2)) \
+ playSound(kEntityMertens, sound1, kFlagDefault); \
+ else \
+ playSound(kEntityMertens, rnd(2) ? sound2 : sound3, kFlagDefault); \
+ } else { \
+ if (rnd(2)) \
+ playSound(kEntityMertens, sound4, kFlagDefault); \
+ else \
+ playSound(kEntityMertens, rnd(2) ? sound5 : sound6, kFlagDefault); \
+ } \
+ _lastWarning[index] = getState()->timeTicks; \
+}
+
+ if (entity != kEntityMertens && entity != kEntityCoudert)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Mertens
+ if (entity == kEntityMertens) {
+
+ switch (compartment) {
+ default:
+ break;
+
+ case kObjectCompartment2:
+ PLAY_WARNING(0, "Con1502A", "Con1500B", "Con1500C", "Con1502", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment3:
+ PLAY_WARNING(1, "Con1501A", "Con1500B", "Con1500C", "Con1501", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment4:
+ PLAY_WARNING(2, "Con1503", "Con1500B", "Con1500C", "Con1503", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ ++_lastWarning[3];
+
+ switch (_lastWarning[3]) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "Con1503C", kFlagDefault);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503E" : "Con1503A", kFlagDefault);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503B" : "Con1503D", kFlagDefault);
+ _lastWarning[3] = 0;
+ break;
+ }
+ }
+
+ return;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Coudert
+ switch (compartment) {
+ default:
+ break;
+
+ case kObjectCompartmentA:
+ if (_lastWarning[4] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1508" : "Jac1508A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentB:
+ if (_lastWarning[5] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTimeCityLinz && getState()->time < kTime2133000))
+ getSound()->playSound(kEntityCoudert, "Jac1507A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1507", kFlagDefault);
+ break;
+
+ case kObjectCompartmentC:
+ if (_lastWarning[6] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().chapter < kChapter3)
+ getSound()->playSound(kEntityCoudert, "Jac1506", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1506A" : "Jac1506B", kFlagDefault);
+ break;
+
+ case kObjectCompartmentD:
+ if (_lastWarning[7] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ getSound()->playSound(kEntityCoudert, "Jac1505", kFlagDefault);
+ break;
+
+ case kObjectCompartmentE:
+ if (_lastWarning[8] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTime2115000 && getState()->time < kTime2133000)) {
+ getSound()->playSound(kEntityCoudert, "Jac1504B", kFlagDefault);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1504" : "Jac1504A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentF:
+ if (_lastWarning[9] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTime2083500 && getState()->time < kTime2133000)) {
+ getSound()->playSound(kEntityCoudert, "Jac1503B", kFlagDefault);
+ break;
+ }
+
+ if (rnd(2) || getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070))
+ getSound()->playSound(kEntityCoudert, "Jac1503", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1503A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentG:
+ if (_lastWarning[10] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (rnd(2) || getEntities()->isInsideCompartment(kEntityMilos, kCarRedSleeping, kPosition_3050))
+ getSound()->playSound(kEntityCoudert, "Jac1502", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1502A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentH:
+ if (_lastWarning[11] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityIvo, kCarRedSleeping, kPosition_2740))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1501", kFlagDefault);
+ break;
+ }
+
+ // Update ticks (Compartments A - H are indexes 4 - 11)
+ _lastWarning[compartment - 28] = getState()->timeTicks;
+}
+
+void SoundManager::excuseMe(EntityIndex entity, EntityIndex entity2, FlagType flag) {
+ if (isBuffered(entity) && entity != kEntityPlayer && entity != kEntityChapters && entity != kEntityTrain)
+ return;
+
+ if (entity2 == kEntityFrancois || entity2 == kEntityMax)
+ return;
+
+ if (entity == kEntityFrancois && getEntityData(kEntityFrancois)->field_4A3 != 30)
+ return;
+
+ if (flag == kFlagNone)
+ flag = getSoundFlag(entity);
+
+ switch (entity) {
+ default:
+ break;
+
+ case kEntityAnna:
+ playSound(kEntityPlayer, "ANN1107A", flag);
+ break;
+
+ case kEntityAugust:
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "AUG1100A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "AUG1100B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "AUG1100C", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, "AUG1100D", flag);
+ break;
+ }
+ break;
+
+ case kEntityMertens:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, (rnd(2) ? "CON1111" : "CON1111A"), flag);
+ } else {
+ if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "CON1110A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "CON1110C", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "CON1110", flag);
+ break;
+ }
+ } else {
+ if (isNight()) {
+ playSound(kEntityPlayer, (getProgress().field_18 == 2 ? "CON1110F" : "CON1110E"));
+ } else {
+ playSound(kEntityPlayer, "CON1110D");
+ }
+ }
+ }
+ break;
+
+ case kEntityCoudert:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, "JAC1111D", flag);
+ } else {
+ if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "JAC1111", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "JAC1111A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "JAC1111B", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, "JAC1111C", flag);
+ break;
+ }
+ } else {
+ playSound(kEntityPlayer, "JAC1113B", flag);
+ }
+ }
+ break;
+
+ case kEntityPascale:
+ playSound(kEntityPlayer, (rnd(2) ? "HDE1002" : "HED1002A"), flag);
+ break;
+
+ case kEntityServers0:
+ case kEntityServers1:
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002" : "WAT1003", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002A" : "WAT1003A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002B" : "WAT1003B", flag);
+ break;
+ }
+ break;
+
+ case kEntityVerges:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, (rnd(2) ? "TRA1113A" : "TRA1113B"));
+ } else {
+ playSound(kEntityPlayer, "TRA1112", flag);
+ }
+ break;
+
+ case kEntityTatiana:
+ playSound(kEntityPlayer, (rnd(2) ? "TAT1102A" : "TAT1102B"), flag);
+ break;
+
+ case kEntityAlexei:
+ playSound(kEntityPlayer, (rnd(2) ? "ALX1099C" : "ALX1099D"), flag);
+ break;
+
+ case kEntityAbbot:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, "ABB3002C", flag);
+ } else {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "ABB3002", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "ABB3002A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "ABB3002B", flag);
+ break;
+ }
+ }
+ break;
+
+ case kEntityVesna:
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "VES1109A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "VES1109B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "VES1109C", flag);
+ break;
+ }
+ break;
+
+ case kEntityKahina:
+ playSound(kEntityPlayer, (rnd(2) ? "KAH1001" : "KAH1001A"), flag);
+ break;
+
+ case kEntityFrancois:
+ case kEntityMmeBoutarel:
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001" : "MME1103A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001A" : "MME1103B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001B" : "MME1103C", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001C" : "MME1103D", flag);
+ break;
+ }
+ break;
+
+ case kEntityBoutarel:
+ playSound(kEntityPlayer, "MRB1104", flag);
+ if (flag > 2)
+ getProgress().eventMetBoutarel = true;
+ break;
+
+ case kEntityRebecca:
+ playSound(kEntityPlayer, (rnd(2) ? "REB1106" : "REB110A"), flag);
+ break;
+
+ case kEntitySophie: {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "SOP1105", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105C" : "SOP1105A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105D" : "SOP1105B", flag);
+ break;
+ }
+ break;
+ }
+
+ case kEntityMahmud:
+ playSound(kEntityPlayer, "MAH1101", flag);
+ break;
+
+ case kEntityYasmin:
+ playSound(kEntityPlayer, "HAR1002", flag);
+ if (flag > 2)
+ getProgress().eventMetYasmin = true;
+ break;
+
+ case kEntityHadija:
+ playSound(kEntityPlayer, (rnd(2) ? "HAR1001" : "HAR1001A"), flag);
+ if (flag > 2)
+ getProgress().eventMetHadija = true;
+ break;
+
+ case kEntityAlouan:
+ playSound(kEntityPlayer, "HAR1004", flag);
+ break;
+ }
+}
+
+void SoundManager::excuseMeCath() {
+ switch(rnd(3)) {
+ default:
+ playSound(kEntityPlayer, "CAT1126B");
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "CAT1126C");
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "CAT1126D");
+ break;
+ }
+}
+
+const char *SoundManager::justCheckingCath() const {
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT5001";
+
+ case 1:
+ return "CAT5001A";
+
+ case 2:
+ return "CAT5001B";
+
+ case 3:
+ return "CAT5001C";
+ }
+
+ return "CAT5001";
+}
+
+const char *SoundManager::wrongDoorCath() const {
+ switch(rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT1125";
+
+ case 1:
+ return "CAT1125A";
+
+ case 2:
+ return "CAT1125B";
+
+ case 3:
+ return "CAT1125C";
+
+ case 4:
+ return "CAT1125D";
+ }
+
+ return "CAT1125";
+}
+
+const char *SoundManager::justAMinuteCath() const {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT1520";
+
+ case 1:
+ return "CAT1521";
+
+ case 2:
+ return "CAT1125"; // ?? is this a bug in the original?
+ }
+
+ return "CAT1520";
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound flags
+//////////////////////////////////////////////////////////////////////////
+SoundManager::FlagType SoundManager::getSoundFlag(EntityIndex entity) const {
+ if (entity == kEntityPlayer)
+ return kFlagDefault;
+
+ if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
+ return kFlagNone;
+
+ // Compute sound value
+ FlagType ret = kFlag2;
+
+ // Get default value if valid
+ int index = ABS(getEntityData(entity)->entityPosition - getEntityData(kEntityPlayer)->entityPosition) / 230;
+ if (index < 32)
+ ret = soundFlags[index];
+
+ if (getEntityData(entity)->location == kLocationOutsideTrain) {
+ if (getEntityData(entity)->car != kCarKronos
+ && !getEntities()->isOutsideAlexeiWindow()
+ && !getEntities()->isOutsideAnnaWindow())
+ return kFlagNone;
+
+ return (FlagType)(ret / 6);
+ }
+
+ switch (getEntityData(entity)->car) {
+ default:
+ break;
+
+ case kCarKronos:
+ if (getEntities()->isInKronosSalon(entity) != getEntities()->isInKronosSalon(kEntityPlayer))
+ ret = (FlagType)(ret * 2);
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInKronosSalon(entity))
+ ret = (FlagType)(ret * 2);
+
+ if (getEntityData(kEntityPlayer)->location
+ && (getEntityData(entity)->entityPosition != kPosition_1 || !getEntities()->isDistanceBetweenEntities(kEntityPlayer, entity, 400)))
+ ret = (FlagType)(ret * 2);
+ break;
+
+ case kCarRestaurant:
+ if (getEntities()->isInSalon(entity) == getEntities()->isInSalon(kEntityPlayer)
+ && (getEntities()->isInRestaurant(entity) != getEntities()->isInRestaurant(kEntityPlayer)))
+ ret = (FlagType)(ret * 2);
+ else
+ ret = (FlagType)(ret * 4);
+ break;
+ }
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Subtitles
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::updateSubtitles() {
+ // TODO: Add mutex ?
+ //warning("SoundManager::updateSubtitles: not implemented!");
+}
+
+void SoundManager::showSubtitles(SoundEntry *entry, Common::String filename) {
+ warning("SoundManager::showSubtitles: not implemented!");
+}
+
+void SoundManager::drawSubtitles(SubtitleManager *subtitle) {
+ warning("SoundManager::drawSubtitles: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playLoopingSound() {
+ warning("SoundManager::playLoopingSound: not implemented!");
+}
+
+void SoundManager::stopAllSound() const {
+ _soundStream->stop();
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/sound.h b/engines/lastexpress/game/sound.h
new file mode 100644
index 0000000000..61326962d0
--- /dev/null
+++ b/engines/lastexpress/game/sound.h
@@ -0,0 +1,355 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SOUND_H
+#define LASTEXPRESS_SOUND_H
+
+/*
+
+ Sound entry: 68 bytes (this is what appears in the savegames)
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - entity
+ uint32 {4} - ??
+ uint32 {4} - ??
+ char {16} - name 1
+ char {16} - name 2
+
+ Sound queue entry: 120 bytes
+ uint16 {2} - status
+ byte {1} - ??
+ byte {1} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - file data pointer
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - archive structure pointer
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - entity
+ uint32 {4} - ??
+ uint32 {4} - ??
+ char {16} - name 1
+ char {16} - name 2
+ uint32 {4} - pointer to next entry in the queue
+ uint32 {4} - subtitle data pointer
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/helpers.h"
+
+#include "common/list.h"
+#include "common/mutex.h"
+#include "common/system.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class StreamedSound;
+class SubtitleManager;
+
+class SoundManager : Common::Serializable {
+public:
+ enum SoundType {
+ kSoundTypeNone = 0,
+ kSoundType1,
+ kSoundType2,
+ kSoundType3,
+ kSoundType4,
+ kSoundType5,
+ kSoundType6,
+ kSoundType7,
+ kSoundType8,
+ kSoundType9,
+ kSoundType10,
+ kSoundType11,
+ kSoundType12,
+ kSoundType13,
+ kSoundType14,
+ kSoundType15,
+ kSoundType16
+ };
+
+ enum FlagType {
+ kFlagInvalid = -1,
+ kFlagNone = 0x0,
+ kFlag2 = 0x2,
+ kFlag3 = 0x3,
+ kFlag4 = 0x4,
+ kFlag5 = 0x5,
+ kFlag6 = 0x6,
+ kFlag7 = 0x7,
+ kFlag8 = 0x8,
+ kFlag9 = 0x9,
+ kFlag10 = 0xA,
+ kFlag11 = 0xB,
+ kFlag12 = 0xC,
+ kFlag13 = 0xD,
+ kFlag14 = 0xE,
+ kFlag15 = 0xF,
+ kFlagDefault = 0x10,
+
+ kFlagType1_2 = 0x1000000,
+ kFlagSteam = 0x1001007,
+ kFlagType13 = 0x3000000,
+ kFlagMenuClock = 0x3080010,
+ kFlagType7 = 0x4000000,
+ kFlagType11 = 0x5000000,
+ kFlagMusic = 0x5000010,
+ kFlagType3 = 0x6000000,
+ kFlagLoop = 0x6001008,
+ kFlagType9 = 0x7000000
+ };
+
+ SoundManager(LastExpressEngine *engine);
+ ~SoundManager();
+
+ // Timer
+ void handleTimer();
+
+ // State
+ void resetState() { _state |= kSoundType1; }
+
+ // Sound queue
+ void updateQueue();
+ void resetQueue(SoundType type1, SoundType type2 = kSoundTypeNone);
+ void clearQueue();
+
+ // Subtitles
+ void updateSubtitles();
+
+ // Entry
+ bool isBuffered(Common::String filename, bool testForEntity = false);
+ bool isBuffered(EntityIndex entity);
+ void setupEntry(SoundType type, EntityIndex index);
+ void processEntry(EntityIndex entity);
+ void processEntry(SoundType type);
+ void processEntry(Common::String filename);
+ void processEntries();
+ void removeFromQueue(Common::String filename);
+ void removeFromQueue(EntityIndex entity);
+ uint32 getEntryTime(EntityIndex index);
+
+ // Misc
+ void unknownFunction4();
+ void clearStatus();
+
+ // Sound playing
+ void playSound(EntityIndex entity, Common::String filename, FlagType flag = kFlagInvalid, byte a4 = 0);
+ bool playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4 = 0);
+ void playSoundEvent(EntityIndex entity, byte action, byte a3 = 0);
+ void playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4);
+ void playSteam(CityIndex index);
+ void playFightSound(byte action, byte a4);
+ void playLocomotiveSound();
+ void playWarningCompartment(EntityIndex entity, ObjectIndex compartment);
+
+ // Dialog & Letters
+ void readText(int id);
+ const char *getDialogName(EntityIndex entity) const;
+
+ // Sound bites
+ void excuseMe(EntityIndex entity, EntityIndex entity2 = kEntityPlayer, FlagType flag = kFlagNone);
+ void excuseMeCath();
+ const char *justCheckingCath() const;
+ const char *wrongDoorCath() const;
+ const char *justAMinuteCath() const;
+
+ // FLags
+ SoundManager::FlagType getSoundFlag(EntityIndex index) const;
+
+ // Debug
+ void stopAllSound() const;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+ uint32 count();
+
+private:
+ typedef int32 *SoundBuffer;
+
+ enum SoundStatus {
+ kSoundStatus_20 = 0x20,
+ kSoundStatusRemoved = 0x200,
+
+ kSoundStatus_8000 = 0x8000,
+ kSoundStatus_100000 = 0x100000,
+ kSoundStatus_40000000 = 0x40000000,
+
+ kSoundStatusClear0 = 0x10,
+ kSoundStatusClear1 = 0x1F,
+ kSoundStatusClear2 = 0x80,
+ kSoundStatusClear3 = 0x200,
+ kSoundStatusClear4 = 0x800,
+ kSoundStatusClearAll = 0xFFFFFFE0
+ };
+
+ enum SoundState {
+ kSoundState0 = 0,
+ kSoundState1 = 1,
+ kSoundState2 = 2
+ };
+
+ union SoundStatusUnion {
+ uint32 status;
+ byte status1;
+ byte status2;
+ byte status3;
+ byte status4;
+
+ SoundStatusUnion() {
+ status = 0;
+ }
+ };
+
+ struct SoundEntry {
+ SoundStatusUnion status;
+ SoundType type; // int
+ //int field_8;
+ //int field_C;
+ //int field_10;
+ //int fileData;
+ //int field_18;
+ int field_1C;
+ uint32 time;
+ //int field_24;
+ //int field_28;
+ Common::SeekableReadStream *stream; // int
+ //int field_30;
+ int field_34;
+ int field_38;
+ int field_3C;
+ int field_40;
+ EntityIndex entity;
+ int field_48;
+ int field_4C;
+ Common::String name1; //char[16];
+ Common::String name2; //char[16];
+ //int next; // offset to the next structure in the list (not used)
+ SubtitleManager *subtitle;
+
+ bool isStreamed; // TEMPORARY
+
+ SoundEntry() {
+ status.status = 0;
+ type = kSoundTypeNone;
+
+ field_1C = 0;
+ time = 0;
+
+ stream = NULL;
+
+ field_34 = 0;
+ field_38 = 0;
+ field_3C = 0;
+ field_40 = 0;
+ entity = kEntityPlayer;
+ field_48 = 0;
+ field_4C = 0;
+
+ subtitle = NULL;
+
+ isStreamed = false;
+ }
+
+ ~SoundEntry() {
+ // Entries that have been queued would have their streamed disposed automatically
+ if (!isStreamed)
+ SAFE_DELETE(stream);
+
+ //delete subtitle;
+ }
+ };
+
+ // Engine
+ LastExpressEngine *_engine;
+
+ // State flag
+ int _state;
+ SoundType _currentType;
+
+ // Sound stream
+ StreamedSound *_soundStream;
+
+ Common::Mutex _mutex;
+
+ // Unknown data
+ uint32 _data0;
+ uint32 _data1;
+ uint32 _data2;
+ uint32 _flag;
+
+ // Filters
+ int32 _buffer[2940]; ///< Static sound buffer
+
+ // Compartment warnings by Mertens or Coudert
+ uint32 _lastWarning[12];
+
+ // Looping sound
+ void playLoopingSound();
+
+ // Sound cache
+ Common::List<SoundEntry *> _cache;
+
+ SoundEntry *getEntry(EntityIndex index);
+ SoundEntry *getEntry(Common::String name);
+ SoundEntry *getEntry(SoundType type);
+
+ void setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4);
+ void setEntryType(SoundEntry *entry, FlagType flag);
+ void setEntryStatus(SoundEntry *entry, FlagType flag) const;
+ bool setupCache(SoundEntry *entry);
+ void loadSoundData(SoundEntry *entry, Common::String name);
+
+ void updateEntry(SoundEntry *entry, uint value) const;
+ void updateEntryState(SoundEntry *entry) const ;
+ void resetEntry(SoundEntry *entry);
+ void removeEntry(SoundEntry *entry);
+
+ // Subtitles
+ void showSubtitles(SoundEntry *entry, Common::String filename);
+ void drawSubtitles(SubtitleManager *subtitle);
+
+ // Sound filter
+ void applyFilter(SoundEntry *entry, SoundBuffer buffer);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SOUND_H
diff --git a/engines/lastexpress/game/state.cpp b/engines/lastexpress/game/state.cpp
new file mode 100644
index 0000000000..45cb2c58ab
--- /dev/null
+++ b/engines/lastexpress/game/state.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+State::State(LastExpressEngine *engine) : _engine(engine), _timer(0) {
+ _inventory = new Inventory(engine);
+ _objects = new Objects(engine);
+ _savepoints = new SavePoints(engine);
+ _state = new GameState();
+ _flags = new Flags();
+}
+
+State::~State() {
+ SAFE_DELETE(_inventory);
+ SAFE_DELETE(_objects);
+ SAFE_DELETE(_savepoints);
+ SAFE_DELETE(_state);
+ SAFE_DELETE(_flags);
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+bool State::isNightTime() const {
+ return (_state->progress.chapter == kChapter1
+ || _state->progress.chapter == kChapter4
+ || (_state->progress.chapter == kChapter5 && !_state->progress.isNightTime));
+}
+
+void State::getHourMinutes(uint32 time, uint8 *hours, uint8 *minutes) {
+ if (hours == NULL || minutes == NULL)
+ error("State::getHourMinutes: invalid parameters passed!");
+
+ *hours = (uint8)((time % 1296000) / 54000);
+ *minutes = (uint8)((time % 54000) / 900);
+}
+
+uint32 State::getPowerOfTwo(uint32 x) {
+ if (!x || (x & 1))
+ return 0;
+
+ uint32 num = 0;
+ do {
+ x >>= 1;
+ num++;
+ } while ((x & 1) == 0);
+
+ return num;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h
new file mode 100644
index 0000000000..da81794ce1
--- /dev/null
+++ b/engines/lastexpress/game/state.h
@@ -0,0 +1,657 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_STATE_H
+#define LASTEXPRESS_STATE_H
+
+#include "lastexpress/shared.h"
+
+#include "common/serializer.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Inventory;
+class Objects;
+class SavePoints;
+
+class State {
+public:
+ struct GameProgress : public Common::Serializable {
+ uint32 field_0;
+ JacketType jacket;
+ bool eventCorpseMovedFromFloor;
+ uint32 field_C;
+ bool eventCorpseFound;
+ uint32 field_14; ///< EntityIndex (used in Gendarmes)
+ uint32 field_18;
+ uint32 portrait;
+ bool eventCorpseThrown;
+ uint32 field_24;
+ uint32 field_28;
+ ChapterIndex chapter;
+ uint32 field_30;
+ bool eventMetAugust;
+ bool isNightTime;
+ uint32 field_3C;
+ uint32 field_40;
+ uint32 field_44;
+ uint32 field_48;
+ uint32 field_4C;
+ bool isTrainRunning;
+ uint32 field_54;
+ uint32 field_58;
+ uint32 field_5C;
+ uint32 field_60;
+ uint32 field_64;
+ uint32 field_68;
+ bool eventMertensAugustWaiting;
+ bool eventMertensKronosInvitation;
+ bool isEggOpen;
+ uint32 field_78; // time?
+ uint32 field_7C;
+ uint32 field_80;
+ uint32 field_84;
+ uint32 field_88;
+ uint32 field_8C;
+ uint32 field_90;
+ uint32 field_94;
+ uint32 field_98;
+ uint32 field_9C;
+ uint32 field_A0;
+ uint32 field_A4;
+ uint32 field_A8;
+ uint32 field_AC;
+ uint32 field_B0;
+ uint32 field_B4;
+ uint32 field_B8;
+ uint32 field_BC;
+ uint32 field_C0;
+ uint32 field_C4;
+ uint32 field_C8;
+ uint32 field_CC;
+ bool eventMetBoutarel;
+ bool eventMetHadija;
+ bool eventMetYasmin;
+ uint32 field_DC;
+ uint32 field_E0;
+ uint32 field_E4;
+ uint32 field_E8;
+ uint32 field_EC;
+ uint32 field_F0;
+ uint32 field_F4;
+ uint32 field_F8;
+ uint32 field_FC;
+ uint32 field_100;
+ uint32 field_104;
+ uint32 field_108;
+ uint32 field_10C;
+ uint32 field_110;
+ uint32 field_114;
+ uint32 field_118;
+ uint32 field_11C;
+ uint32 field_120;
+ uint32 field_124;
+ uint32 field_128;
+ uint32 field_12C;
+ uint32 field_130;
+ uint32 field_134;
+ uint32 field_138;
+ uint32 field_13C;
+ uint32 field_140;
+ uint32 field_144;
+ uint32 field_148;
+ uint32 field_14C;
+ uint32 field_150;
+ uint32 field_154;
+ uint32 field_158;
+ uint32 field_15C;
+ uint32 field_160;
+ uint32 field_164;
+ uint32 field_168;
+ uint32 field_16C;
+ uint32 field_170;
+ uint32 field_174;
+ uint32 field_178;
+ uint32 field_17C;
+ uint32 field_180;
+ uint32 field_184;
+ uint32 field_188;
+ uint32 field_18C;
+ uint32 field_190;
+ uint32 field_194;
+ uint32 field_198;
+ uint32 field_19C;
+ uint32 field_1A0;
+ uint32 field_1A4;
+ uint32 field_1A8;
+ uint32 field_1AC;
+ uint32 field_1B0;
+ uint32 field_1B4;
+ uint32 field_1B8;
+ uint32 field_1BC;
+ uint32 field_1C0;
+ uint32 field_1C4;
+ uint32 field_1C8;
+ uint32 field_1CC;
+ uint32 field_1D0;
+ uint32 field_1D4;
+ uint32 field_1D8;
+ uint32 field_1DC;
+ uint32 field_1E0;
+ uint32 field_1E4;
+ uint32 field_1E8;
+ uint32 field_1EC;
+ uint32 field_1F0;
+ uint32 field_1F4;
+ uint32 field_1F8;
+ uint32 field_1FC;
+
+ GameProgress() {
+ field_0 = 0;
+ jacket = kJacketOriginal;
+ eventCorpseMovedFromFloor = false;
+ field_C = 0;
+ eventCorpseFound = false;
+ field_14 = 0; // 5
+ field_18 = 0;
+ portrait = _defaultPortrait;
+ eventCorpseThrown = false;
+ field_24 = 0;
+ field_28 = 0; // 10
+ chapter = kChapter1;
+ field_30 = 0;
+ eventMetAugust = false;
+ isNightTime = false;
+ field_3C = 0; // 15
+ field_40 = 0;
+ field_44 = 0;
+ field_48 = 0;
+ field_4C = 0;
+ isTrainRunning = false; // 20
+ field_54 = 0;
+ field_58 = 0;
+ field_5C = 0;
+ field_60 = 0;
+ field_64 = 0; // 25
+ field_68 = 0;
+ eventMertensAugustWaiting = false;
+ eventMertensKronosInvitation = false;
+ isEggOpen = false;
+ field_78 = 0; // 30
+ field_7C = 0;
+ field_80 = 0;
+ field_84 = 0;
+ field_88 = 0;
+ field_8C = 0; // 35
+ field_90 = 0;
+ field_94 = 0;
+ field_98 = 0;
+ field_9C = 0;
+ field_A0 = 0; // 40
+ field_A4 = 0;
+ field_A8 = 0;
+ field_AC = 0;
+ field_B0 = 0;
+ field_B4 = 0; // 45
+ field_B8 = 0;
+ field_BC = 0;
+ field_C0 = 0;
+ field_C4 = 0;
+ field_C8 = 0; // 50
+ field_CC = 0;
+ eventMetBoutarel = false;
+ eventMetHadija = false;
+ eventMetYasmin = false;
+ field_DC = 0; // 55
+ field_E0 = 0;
+ field_E4 = 0;
+ field_E8 = 0;
+ field_EC = 0;
+ field_F0 = 0; // 60
+ field_F4 = 0;
+ field_F8 = 0;
+ field_FC = 0;
+ field_100 = 0;
+ field_104 = 0; // 65
+ field_108 = 0;
+ field_10C = 0;
+ field_110 = 0;
+ field_114 = 0;
+ field_118 = 0; // 70
+ field_11C = 0;
+ field_120 = 0;
+ field_124 = 0;
+ field_128 = 0;
+ field_12C = 0; // 75
+ field_130 = 0;
+ field_134 = 0;
+ field_138 = 0;
+ field_13C = 0;
+ field_140 = 0; // 80
+ field_144 = 0;
+ field_148 = 0;
+ field_14C = 0;
+ field_150 = 0;
+ field_154 = 0; // 85
+ field_158 = 0;
+ field_15C = 0;
+ field_160 = 0;
+ field_164 = 0;
+ field_168 = 0; // 90
+ field_16C = 0;
+ field_170 = 0;
+ field_174 = 0;
+ field_178 = 0;
+ field_17C = 0; // 95
+ field_180 = 0;
+ field_184 = 0;
+ field_188 = 0;
+ field_18C = 0;
+ field_190 = 0; // 100
+ field_194 = 0;
+ field_198 = 0;
+ field_19C = 0;
+ field_1A0 = 0;
+ field_1A4 = 0; // 105
+ field_1A8 = 0;
+ field_1AC = 0;
+ field_1B0 = 0;
+ field_1B4 = 0;
+ field_1B8 = 0; // 110
+ field_1BC = 0;
+ field_1C0 = 0;
+ field_1C4 = 0;
+ field_1C8 = 0;
+ field_1CC = 0; // 115
+ field_1D0 = 0;
+ field_1D4 = 0;
+ field_1D8 = 0;
+ field_1DC = 0;
+ field_1E0 = 0; // 120
+ field_1E4 = 0;
+ field_1E8 = 0;
+ field_1EC = 0;
+ field_1F0 = 0;
+ field_1F4 = 0; // 125
+ field_1F8 = 0;
+ field_1FC = 0;
+ }
+
+ /**
+ * Query if if a progress value is equal to the specified value.
+ *
+ * Note: This is necessary because we store different types in the progress structure
+ * and need to test a value based on an index in Action::getCursor()
+ *
+ * @param index Zero-based index of the progress structure entry
+ * @param val The value.
+ *
+ * @return true if equal, false if not.
+ */
+ bool isEqual(uint index, uint val) {
+ return getValueName(index) == val;
+ }
+
+ uint32 getValueName(uint index, Common::String *name = NULL) {
+ #define EXPOSE_VALUE(idx, entryName) \
+ case idx: { \
+ if (name) (*name) = "" #entryName; \
+ return (uint32)entryName; \
+ }
+
+ switch (index) {
+ default:
+ error("GameProgress::isEqual: invalid index value (was: %d, max:127)", index);
+ break;
+
+ EXPOSE_VALUE(0, field_0);
+ EXPOSE_VALUE(1, jacket);
+ EXPOSE_VALUE(2, eventCorpseMovedFromFloor);
+ EXPOSE_VALUE(3, field_C);
+ EXPOSE_VALUE(4, eventCorpseFound);
+ EXPOSE_VALUE(5, field_14);
+ EXPOSE_VALUE(6, field_18);
+ EXPOSE_VALUE(7, portrait);
+ EXPOSE_VALUE(8, eventCorpseThrown);
+ EXPOSE_VALUE(9, field_24);
+ EXPOSE_VALUE(10, field_28);
+ EXPOSE_VALUE(11, chapter);
+ EXPOSE_VALUE(12, field_30);
+ EXPOSE_VALUE(13, eventMetAugust);
+ EXPOSE_VALUE(14, isNightTime);
+ EXPOSE_VALUE(15, field_3C);
+ EXPOSE_VALUE(16, field_40);
+ EXPOSE_VALUE(17, field_44);
+ EXPOSE_VALUE(18, field_48);
+ EXPOSE_VALUE(19, field_4C);
+ EXPOSE_VALUE(20, isTrainRunning);
+ EXPOSE_VALUE(21, field_54);
+ EXPOSE_VALUE(22, field_58);
+ EXPOSE_VALUE(23, field_5C);
+ EXPOSE_VALUE(24, field_60);
+ EXPOSE_VALUE(25, field_64);
+ EXPOSE_VALUE(26, field_68);
+ EXPOSE_VALUE(27, eventMertensAugustWaiting);
+ EXPOSE_VALUE(28, eventMertensKronosInvitation);
+ EXPOSE_VALUE(29, isEggOpen);
+ EXPOSE_VALUE(30, field_78);
+ EXPOSE_VALUE(31, field_7C);
+ EXPOSE_VALUE(32, field_80);
+ EXPOSE_VALUE(33, field_84);
+ EXPOSE_VALUE(34, field_88);
+ EXPOSE_VALUE(35, field_8C);
+ EXPOSE_VALUE(36, field_90);
+ EXPOSE_VALUE(37, field_94);
+ EXPOSE_VALUE(38, field_98);
+ EXPOSE_VALUE(39, field_9C);
+ EXPOSE_VALUE(40, field_A0);
+ EXPOSE_VALUE(41, field_A4);
+ EXPOSE_VALUE(42, field_A8);
+ EXPOSE_VALUE(43, field_AC);
+ EXPOSE_VALUE(44, field_B0);
+ EXPOSE_VALUE(45, field_B4);
+ EXPOSE_VALUE(46, field_B8);
+ EXPOSE_VALUE(47, field_BC);
+ EXPOSE_VALUE(48, field_C0);
+ EXPOSE_VALUE(49, field_C4);
+ EXPOSE_VALUE(50, field_C8);
+ EXPOSE_VALUE(51, field_CC);
+ EXPOSE_VALUE(52, eventMetBoutarel);
+ EXPOSE_VALUE(53, eventMetHadija);
+ EXPOSE_VALUE(54, eventMetYasmin);
+ EXPOSE_VALUE(55, field_DC);
+ EXPOSE_VALUE(56, field_E0);
+ EXPOSE_VALUE(57, field_E4);
+ EXPOSE_VALUE(58, field_E8);
+ EXPOSE_VALUE(59, field_EC);
+ EXPOSE_VALUE(60, field_F0);
+ EXPOSE_VALUE(61, field_F4);
+ EXPOSE_VALUE(62, field_F8);
+ EXPOSE_VALUE(63, field_FC);
+ EXPOSE_VALUE(64, field_100);
+ EXPOSE_VALUE(65, field_104);
+ EXPOSE_VALUE(66, field_108);
+ EXPOSE_VALUE(67, field_10C);
+ EXPOSE_VALUE(68, field_110);
+ EXPOSE_VALUE(69, field_114);
+ EXPOSE_VALUE(70, field_118);
+ EXPOSE_VALUE(71, field_11C);
+ EXPOSE_VALUE(72, field_120);
+ EXPOSE_VALUE(73, field_124);
+ EXPOSE_VALUE(74, field_128);
+ EXPOSE_VALUE(75, field_12C);
+ EXPOSE_VALUE(76, field_130);
+ EXPOSE_VALUE(77, field_134);
+ EXPOSE_VALUE(78, field_138);
+ EXPOSE_VALUE(79, field_13C);
+ EXPOSE_VALUE(80, field_140);
+ EXPOSE_VALUE(81, field_144);
+ EXPOSE_VALUE(82, field_148);
+ EXPOSE_VALUE(83, field_14C);
+ EXPOSE_VALUE(84, field_150);
+ EXPOSE_VALUE(85, field_154);
+ EXPOSE_VALUE(86, field_158);
+ EXPOSE_VALUE(87, field_15C);
+ EXPOSE_VALUE(88, field_160);
+ EXPOSE_VALUE(89, field_164);
+ EXPOSE_VALUE(90, field_168);
+ EXPOSE_VALUE(91, field_16C);
+ EXPOSE_VALUE(92, field_170);
+ EXPOSE_VALUE(93, field_174);
+ EXPOSE_VALUE(94, field_178);
+ EXPOSE_VALUE(95, field_17C);
+ EXPOSE_VALUE(96, field_180);
+ EXPOSE_VALUE(97, field_184);
+ EXPOSE_VALUE(98, field_188);
+ EXPOSE_VALUE(99, field_18C);
+ EXPOSE_VALUE(100, field_190);
+ EXPOSE_VALUE(101, field_194);
+ EXPOSE_VALUE(102, field_198);
+ EXPOSE_VALUE(103, field_19C);
+ EXPOSE_VALUE(104, field_1A0);
+ EXPOSE_VALUE(105, field_1A4);
+ EXPOSE_VALUE(106, field_1A8);
+ EXPOSE_VALUE(107, field_1AC);
+ EXPOSE_VALUE(108, field_1B0);
+ EXPOSE_VALUE(109, field_1B4);
+ EXPOSE_VALUE(110, field_1B8);
+ EXPOSE_VALUE(111, field_1BC);
+ EXPOSE_VALUE(112, field_1C0);
+ EXPOSE_VALUE(113, field_1C4);
+ EXPOSE_VALUE(114, field_1C8);
+ EXPOSE_VALUE(115, field_1CC);
+ EXPOSE_VALUE(116, field_1D0);
+ EXPOSE_VALUE(117, field_1D4);
+ EXPOSE_VALUE(118, field_1D8);
+ EXPOSE_VALUE(119, field_1DC);
+ EXPOSE_VALUE(120, field_1E0);
+ EXPOSE_VALUE(121, field_1E4);
+ EXPOSE_VALUE(122, field_1E8);
+ EXPOSE_VALUE(123, field_1EC);
+ EXPOSE_VALUE(124, field_1F0);
+ EXPOSE_VALUE(125, field_1F4);
+ EXPOSE_VALUE(126, field_1F8);
+ EXPOSE_VALUE(127, field_1FC);
+ }
+ }
+
+ Common::String toString() {
+ Common::String ret = "";
+
+ for (uint i = 0; i < 128; i++) {
+ Common::String name = "";
+ uint val = getValueName(i, &name);
+ ret += Common::String::format("(%03d) %s = %d\n", i, name.c_str(), val);
+ }
+
+ return ret;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < 128; i++) {
+ uint32 val = getValueName(i);
+ s.syncAsUint32LE(val);
+ }
+ }
+ };
+
+ struct GameState : public Common::Serializable {
+ // Header
+ uint32 brightness;
+ uint32 volume;
+
+ // Game data
+ uint32 field_0;
+ TimeValue time;
+ uint32 timeDelta;
+ uint32 timeTicks;
+ bool sceneUseBackup; // byte
+ SceneIndex scene; // uint32
+ SceneIndex sceneBackup; // uint32
+ SceneIndex sceneBackup2; // uin32
+
+ GameProgress progress;
+ byte events[512];
+
+ GameState() {
+ brightness = _defaultBrigthness;
+ volume = _defaultVolume;
+
+ //Game data
+ time = kTimeCityParis;
+ timeDelta = _defaultTimeDelta;
+ timeTicks = 0;
+ sceneUseBackup = false;
+ scene = kSceneDefault;
+ sceneBackup = kSceneNone;
+ sceneBackup2 = kSceneNone;
+
+ // Clear game events
+ memset(events, 0, 512*sizeof(byte));
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String ret = "";
+
+ uint8 hours = 0;
+ uint8 minutes = 0;
+ getHourMinutes(time, &hours, &minutes);
+
+ ret += Common::String::format("Time: %d (%d:%d) - Time delta: %d - Ticks: %d\n", time, hours, minutes, timeDelta, timeTicks);
+ ret += Common::String::format("Brightness: %d - Volume: %d - UseBackup: %d\n", brightness, volume, sceneUseBackup);
+ ret += Common::String::format("Scene: %d - Scene backup: %d - Scene backup 2: %d\n", scene, sceneBackup, sceneBackup2);
+
+ return ret;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(time);
+ s.syncAsUint32LE(timeDelta);
+ s.syncAsUint32LE(timeTicks);
+ s.syncAsUint32LE(scene);
+ s.syncAsByte(sceneUseBackup);
+ s.syncAsUint32LE(sceneBackup);
+ s.syncAsUint32LE(sceneBackup2);
+ }
+
+ void syncEvents(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(events); i++)
+ s.syncAsByte(events[i]);
+ }
+ };
+
+ struct Flags {
+ bool flag_0;
+ bool flag_3;
+ bool flag_4;
+ bool flag_5;
+
+ bool frameInterval;
+
+ bool isGameRunning;
+
+ // Mouse flags
+ bool mouseLeftClick;
+ bool mouseRightClick;
+
+ bool flag_entities_0;
+ bool flag_entities_1;
+
+ bool shouldRedraw;
+ bool shouldDrawEggOrHourGlass;
+
+ Flags() {
+ flag_0 = false;
+ flag_3 = false;
+ flag_4 = false;
+ flag_5 = false;
+
+ frameInterval = false;
+
+ isGameRunning = false;
+
+ mouseRightClick = false;
+ mouseLeftClick = false;
+
+ flag_entities_0 = false;
+ flag_entities_1 = false;
+
+ shouldRedraw = false;
+ shouldDrawEggOrHourGlass = false;
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String ret = "";
+
+ ret += Common::String::format("Unknown: 0:%02d - 3:%02d - 4:%02d - 5:%02d\n", flag_0, flag_3, flag_4, flag_5);
+ ret += Common::String::format("FrameInterval: %02d - ShouldRedraw:%02d - ShouldDrawEggOrHourGlass:%02d\n", frameInterval, shouldRedraw, shouldDrawEggOrHourGlass);
+ ret += Common::String::format("IsGameRunning: %02d\n", isGameRunning);
+ ret += Common::String::format("Mouse: RightClick:%02d - LeftClick:%02d\n", mouseRightClick, mouseLeftClick);
+ ret += Common::String::format("Entities: 0:%02d - 1:%02d\n", flag_entities_0, flag_entities_1);
+
+ return ret;
+ }
+ };
+
+ State(LastExpressEngine *engine);
+ ~State();
+
+ // Accessors
+ Inventory *getGameInventory() { return _inventory; }
+ Objects *getGameObjects() { return _objects; }
+ SavePoints *getGameSavePoints() { return _savepoints; }
+ GameState *getGameState() { return _state; }
+ Flags *getGameFlags() { return _flags; }
+
+ // Time checks
+ bool isNightTime() const;
+
+ // Timer
+ int getTimer() { return _timer; }
+ void setTimer(int val) { _timer = val; }
+
+ // Coordinates
+ void setCoordinates(Common::Point coords) { _coords = coords; }
+ const Common::Point getCoordinates() { return _coords; }
+
+ // Helpers
+ static uint32 getPowerOfTwo(uint32 x);
+ static void getHourMinutes(uint32 time, uint8 *hours, uint8 *minutes);
+
+private:
+ static const uint32 _defaultBrigthness = 3;
+ static const uint32 _defaultVolume = 7;
+ static const uint32 _defaultTimeDelta = 3;
+ static const uint32 _defaultPortrait = 32;
+
+ LastExpressEngine *_engine;
+
+ // Timer
+ int _timer;
+
+ Flags *_flags; ///< Flags
+ Inventory *_inventory; ///< Inventory
+ Objects *_objects; ///< Objects
+ SavePoints *_savepoints; ///< SavePoints
+ GameState *_state; ///< State
+ Common::Point _coords; ///< Current coordinates
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_STATE_H
diff --git a/engines/lastexpress/graphics.cpp b/engines/lastexpress/graphics.cpp
new file mode 100644
index 0000000000..e5a69d16ea
--- /dev/null
+++ b/engines/lastexpress/graphics.cpp
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/graphics.h"
+
+#include "common/system.h"
+
+namespace LastExpress {
+
+#define COLOR_KEY 0xFFFF
+
+GraphicsManager::GraphicsManager() : _changed(false) {
+ _screen.create(640, 480, 2);
+
+ // Create the game surfaces
+ _backgroundA.create(640, 480, 2);
+ _backgroundC.create(640, 480, 2);
+ _overlay.create(640, 480, 2);
+ _inventory.create(640, 480, 2);
+
+ clear(kBackgroundAll);
+}
+
+GraphicsManager::~GraphicsManager() {
+ // Free the game surfaces
+ _screen.free();
+ _backgroundA.free();
+ _backgroundC.free();
+ _overlay.free();
+ _inventory.free();
+}
+
+void GraphicsManager::update() {
+ // Update the screen if needed and reset the status
+ if (_changed) {
+ mergePlanes();
+ updateScreen();
+ _changed = false;
+ }
+}
+
+void GraphicsManager::change() {
+ _changed = true;
+}
+
+void GraphicsManager::clear(BackgroundType type) {
+ clear(type, Common::Rect(640, 480));
+}
+
+void GraphicsManager::clear(BackgroundType type, const Common::Rect &rect) {
+ switch (type) {
+ default:
+ error("GraphicsManager::clear() - Unknown background type: %d", type);
+ break;
+
+ case kBackgroundA:
+ case kBackgroundC:
+ case kBackgroundOverlay:
+ case kBackgroundInventory:
+ getSurface(type)->fillRect(rect, COLOR_KEY);
+ break;
+
+ case kBackgroundAll:
+ _backgroundA.fillRect(rect, COLOR_KEY);
+ _backgroundC.fillRect(rect, COLOR_KEY);
+ _overlay.fillRect(rect, COLOR_KEY);
+ _inventory.fillRect(rect, COLOR_KEY);
+ break;
+ }
+}
+
+bool GraphicsManager::draw(Drawable *drawable, BackgroundType type, bool transition) {
+ // TODO handle transition properly
+ if (transition)
+ clear(type);
+
+ // TODO store rect for later use
+ Common::Rect rect = drawable->draw(getSurface(type));
+
+ return (!rect.isEmpty());
+}
+
+Graphics::Surface *GraphicsManager::getSurface(BackgroundType type) {
+ switch (type) {
+ default:
+ error("GraphicsManager::getSurface() - Unknown surface type: %d", type);
+ break;
+
+ case kBackgroundA:
+ return &_backgroundA;
+
+ case kBackgroundC:
+ return &_backgroundC;
+
+ case kBackgroundOverlay:
+ return &_overlay;
+
+ case kBackgroundInventory:
+ return &_inventory;
+
+ case kBackgroundAll:
+ error("GraphicsManager::getSurface() - cannot return a surface for kBackgroundAll!");
+ break;
+ }
+}
+
+// TODO optimize to only merge dirty rects
+void GraphicsManager::mergePlanes() {
+ // Clear screen surface
+ _screen.fillRect(Common::Rect(640, 480), 0);
+
+ uint16 *screen = (uint16 *)_screen.pixels;
+ uint16 *inventory = (uint16 *)_inventory.pixels;
+ uint16 *overlay = (uint16 *)_overlay.pixels;
+ uint16 *backgroundC = (uint16 *)_backgroundC.pixels;
+ uint16 *backgroundA = (uint16 *)_backgroundA.pixels;
+
+ for (int i = 0; i < 640 * 480; i++) {
+
+ if (*inventory != COLOR_KEY)
+ *screen = *inventory;
+ else if (*overlay != COLOR_KEY)
+ *screen = *overlay;
+ else if (*backgroundA != COLOR_KEY)
+ *screen = *backgroundA;
+ else if (*backgroundC != COLOR_KEY)
+ *screen = *backgroundC;
+ else
+ *screen = 0;
+
+ inventory++;
+ screen++;
+ overlay++;
+ backgroundA++;
+ backgroundC++;
+ }
+}
+
+void GraphicsManager::updateScreen() {
+ g_system->fillScreen(0);
+ g_system->copyRectToScreen((byte *)_screen.getBasePtr(0, 0), 640 * 2, 0, 0, 640, 480);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/graphics.h b/engines/lastexpress/graphics.h
new file mode 100644
index 0000000000..5231d00f6f
--- /dev/null
+++ b/engines/lastexpress/graphics.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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_GRAPHICS_H
+#define LASTEXPRESS_GRAPHICS_H
+
+#include "lastexpress/drawable.h"
+
+namespace LastExpress {
+
+class GraphicsManager {
+public:
+ enum BackgroundType {
+ kBackgroundA,
+ kBackgroundC,
+ kBackgroundOverlay,
+ kBackgroundInventory,
+ kBackgroundAll
+ };
+
+ GraphicsManager();
+ ~GraphicsManager();
+
+ // Update the screen
+ void update();
+
+ // Signal a change to the screen, will cause the planes to be remerged
+ void change();
+
+ // Clear some screen parts
+ void clear(BackgroundType type);
+ void clear(BackgroundType type, const Common::Rect &rect);
+
+ // FIXME this is there for animation until we change it to use the graphic surface here instead of its private ones.
+ Graphics::Surface _screen; // Actual screen surface
+
+ bool draw(Drawable *drawable, BackgroundType type, bool transition = false);
+
+private:
+ Graphics::Surface _backgroundA; // Background A
+ Graphics::Surface _backgroundC; // Background C
+ Graphics::Surface _overlay; // Overlay
+ Graphics::Surface _inventory; // Overlay
+
+ void mergePlanes();
+ void updateScreen();
+ Graphics::Surface *getSurface(BackgroundType type);
+
+ bool _changed;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_GRAPHICS_H
diff --git a/engines/lastexpress/helpers.h b/engines/lastexpress/helpers.h
new file mode 100644
index 0000000000..f9e61b6fe6
--- /dev/null
+++ b/engines/lastexpress/helpers.h
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_HELPERS_H
+#define LASTEXPRESS_HELPERS_H
+
+//////////////////////////////////////////////////////////////////////////
+// Misc helpers
+//////////////////////////////////////////////////////////////////////////
+
+// Misc
+#define getArchive(name) _engine->getResourceManager()->getFileStream(name)
+#define rnd(value) _engine->getRandom().getRandomNumber(value - 1)
+
+// Engine subclasses
+#define getLogic() _engine->getGameLogic()
+#define getMenu() _engine->getGameMenu()
+
+// Logic
+#define getAction() getLogic()->getGameAction()
+#define getBeetle() getLogic()->getGameBeetle()
+#define getFight() getLogic()->getGameFight()
+#define getEntities() getLogic()->getGameEntities()
+#define getSaveLoad() getLogic()->getGameSaveLoad()
+#define isNight() getLogic()->getGameState()->isNightTime()
+
+// State
+#define getState() getLogic()->getGameState()->getGameState()
+#define getEvent(id) getState()->events[id]
+#define getFlags() getLogic()->getGameState()->getGameFlags()
+#define getInventory() getLogic()->getGameState()->getGameInventory()
+#define getObjects() getLogic()->getGameState()->getGameObjects()
+#define getProgress() getState()->progress
+#define getSavePoints() getLogic()->getGameState()->getGameSavePoints()
+#define getGlobalTimer() getLogic()->getGameState()->getTimer()
+#define setGlobalTimer(timer) getLogic()->getGameState()->setTimer(timer)
+#define setCoords(coords) getLogic()->getGameState()->setCoordinates(coords)
+#define getCoords() getLogic()->getGameState()->getCoordinates()
+#define setFrameCount(count) _engine->setFrameCounter(count)
+#define getFrameCount() _engine->getFrameCounter()
+
+// Scenes
+#define getScenes() _engine->getSceneManager()
+
+// Sound
+#define getSound() _engine->getSoundManager()
+
+// Others
+#define getEntityData(entity) getEntities()->getData(entity)
+
+//////////////////////////////////////////////////////////////////////////
+// Graphics
+//////////////////////////////////////////////////////////////////////////
+
+// Sequences
+#define loadSequence(name) Sequence::load(name, getArchive(name))
+#define loadSequence1(name, field30) Sequence::load(name, getArchive(name), field30)
+
+#define clearBg(type) _engine->getGraphicsManager()->clear(type)
+#define showScene(index, type) _engine->getGraphicsManager()->draw(getScenes()->get(index), type);
+
+#define askForRedraw() _engine->getGraphicsManager()->change()
+#define redrawScreen() do { _engine->getGraphicsManager()->update(); _engine->_system->updateScreen(); } while (false)
+
+// Used to delete entity sequences
+#define SAFE_DELETE(_p) do { delete (_p); (_p) = NULL; } while (false)
+
+//////////////////////////////////////////////////////////////////////////
+// Output
+//////////////////////////////////////////////////////////////////////////
+extern const char *g_actionNames[];
+extern const char *g_directionNames[];
+extern const char *g_entityNames[];
+
+#define ACTION_NAME(action) (action > 18 ? Common::String::format("%d", action).c_str() : g_actionNames[action])
+#define DIRECTION_NAME(direction) (direction >= 6 ? "INVALID" : g_directionNames[direction])
+#define ENTITY_NAME(index) (index >= 40 ? "INVALID" : g_entityNames[index])
+
+
+#endif // LASTEXPRESS_HELPERS_H
diff --git a/engines/lastexpress/lastexpress.cpp b/engines/lastexpress/lastexpress.cpp
new file mode 100644
index 0000000000..91ef2f799d
--- /dev/null
+++ b/engines/lastexpress/lastexpress.cpp
@@ -0,0 +1,314 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/lastexpress.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/resource.h"
+
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/EventRecorder.h"
+
+#include "engines/util.h"
+
+const char *g_actionNames[] = {"None", "Action1", "Action2", "ExitCompartment", "Action4", "ExcuseMeCath", "ExcuseMe", "INVALID", "Knock", "OpenDoor", "Action10", "Action11", "Default", "INVALID", "INVALID", "INVALID", "Action16", "DrawScene", "Callback"};
+const char *g_directionNames[] = { "None", "Up", "Down", "Left", "Right", "Switch"};
+const char *g_entityNames[] = { "Player", "Anna", "August", "Mertens", "Coudert", "Pascale", "Servers0", "Servers1", "Cooks", "Verges", "Tatiana", "Vassili", "Alexei", "Abbot", "Milos", "Vesna", "Ivo", "Salko", "Kronos", "Kahina", "Francois", "MmeBoutarel", "Boutarel", "Rebecca", "Sophie", "Mahmud", "Yasmin", "Hadija", "Alouan", "Gendarmes", "Max", "Chapters", "Train", "Tables0", "Tables1", "Tables2", "Tables3", "Tables4", "Tables5", "Entity39"};
+
+
+namespace LastExpress {
+
+LastExpressEngine::LastExpressEngine(OSystem *syst, const ADGameDescription *gd) :
+ Engine(syst), _gameDescription(gd), _debugger(NULL), _cursor(NULL),
+ _font(NULL), _logic(NULL), _menu(NULL), _frameCounter(0), _lastFrameCount(0),
+ _graphicsMan(NULL), _resMan(NULL), _sceneMan(NULL), _soundMan(NULL),
+ _eventMouse(NULL), _eventTick(NULL), _eventMouseBackup(NULL), _eventTickBackup(NULL) {
+
+ // Adding the default directories
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "data");
+
+ // Initialize the custom debug levels
+ DebugMan.addDebugChannel(kLastExpressDebugAll, "All", "Debug everything");
+ DebugMan.addDebugChannel(kLastExpressDebugGraphics, "Graphics", "Debug graphics & animation/sequence playback");
+ DebugMan.addDebugChannel(kLastExpressDebugResource, "Resource", "Debug resource management");
+ DebugMan.addDebugChannel(kLastExpressDebugCursor, "Cursor", "Debug cursor handling");
+ DebugMan.addDebugChannel(kLastExpressDebugSound, "Sound", "Debug sound playback");
+ DebugMan.addDebugChannel(kLastExpressDebugSubtitle, "Subtitle", "Debug subtitles");
+ DebugMan.addDebugChannel(kLastExpressDebugSavegame, "Savegame", "Debug savegames");
+ DebugMan.addDebugChannel(kLastExpressDebugLogic, "Logic", "Debug logic");
+ DebugMan.addDebugChannel(kLastExpressDebugScenes, "Scenes", "Debug scenes & hotspots");
+ DebugMan.addDebugChannel(kLastExpressDebugUnknown, "Unknown", "Debug unknown data");
+
+ g_eventRec.registerRandomSource(_random, "lastexpress");
+}
+
+LastExpressEngine::~LastExpressEngine() {
+ _timer->removeTimerProc(&soundTimer);
+
+ // Delete the remaining objects
+ SAFE_DELETE(_cursor);
+ SAFE_DELETE(_font);
+ SAFE_DELETE(_logic);
+ SAFE_DELETE(_menu);
+ SAFE_DELETE(_graphicsMan);
+ SAFE_DELETE(_resMan);
+ SAFE_DELETE(_sceneMan);
+ SAFE_DELETE(_soundMan);
+ SAFE_DELETE(_debugger);
+
+ // Cleanup event handlers
+ SAFE_DELETE(_eventMouse);
+ SAFE_DELETE(_eventTick);
+ SAFE_DELETE(_eventMouseBackup);
+ SAFE_DELETE(_eventTickBackup);
+
+ // Zero passed pointers
+ _gameDescription = NULL;
+}
+
+// TODO: which error should we return when some game files are missing/corrupted?
+Common::Error LastExpressEngine::run() {
+ // Initialize the graphics
+ const Graphics::PixelFormat dataPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ initGraphics(640, 480, true, &dataPixelFormat);
+
+ // We do not support color conversion
+ if (_system->getScreenFormat() != dataPixelFormat)
+ return Common::kUnsupportedColorMode;
+
+ // Create debugger. It requires GFX to be initialized
+ _debugger = new Debugger(this);
+
+ // Start the resource and graphics managers
+ _resMan = new ResourceManager(isDemo());
+ if (!_resMan->loadArchive(kArchiveCd1))
+ return Common::kNoGameDataFoundError;
+
+ _graphicsMan = new GraphicsManager();
+
+ // Load the cursor data
+ _cursor = _resMan->loadCursor();
+ if (!_cursor)
+ return Common::kNoGameDataFoundError;
+
+ // Load the font data
+ _font = _resMan->loadFont();
+ if (!_font)
+ return Common::kNoGameDataFoundError;
+
+ // Start scene manager
+ _sceneMan = new SceneManager(this);
+ _sceneMan->loadSceneDataFile(kArchiveCd1);
+
+ // Game logic
+ _logic = new Logic(this);
+
+ // Start sound manager and setup timer
+ _soundMan = new SoundManager(this);
+ _timer->installTimerProc(&soundTimer, 17, this);
+
+ // Menu
+ _menu = new Menu(this);
+ _menu->show(false, kSavegameTypeIndex, 0);
+
+ while (!shouldQuit()) {
+ _soundMan->updateQueue();
+ _soundMan->updateSubtitles();
+
+ if (handleEvents())
+ continue;
+ }
+
+ return Common::kNoError;
+}
+
+void LastExpressEngine::pollEvents() {
+ Common::Event ev;
+ _eventMan->pollEvent(ev);
+
+ switch (ev.type) {
+
+ case Common::EVENT_LBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseLeftClick = true;
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseRightClick = true;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool LastExpressEngine::handleEvents() {
+ // Make sure all the subsystems have been initialized
+ if (!_debugger || !_graphicsMan)
+ error("LastExpressEngine::handleEvents: called before the required subsystems have been initialized!");
+
+ // Execute stored commands
+ if (_debugger->hasCommand()) {
+ _debugger->callCommand();
+
+ // re-attach the debugger
+ _debugger->attach();
+ }
+
+ // Show the debugger if required
+ _debugger->onFrame();
+
+ // Handle input
+ Common::Event ev;
+ while (_eventMan->pollEvent(ev)) {
+ switch (ev.type) {
+
+ case Common::EVENT_KEYDOWN:
+ // CTRL-D: Attach the debugger
+ if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
+ _debugger->attach();
+
+ //// DEBUG: Quit game on escape
+ //if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+ // quitGame();
+
+ break;
+
+ case Common::EVENT_MAINMENU:
+ // Closing the GMM
+
+ case Common::EVENT_LBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseLeftClick = true;
+
+ // Adjust frameInterval flag
+ if (_frameCounter < _lastFrameCount + 30)
+ getGameLogic()->getGameState()->getGameFlags()->frameInterval = true;
+ _lastFrameCount = _frameCounter;
+
+ if (_eventMouse && _eventMouse->isValid())
+ (*_eventMouse)(ev);
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseRightClick = true;
+ if (_eventMouse && _eventMouse->isValid())
+ (*_eventMouse)(ev);
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ if (_eventMouse && _eventMouse->isValid())
+ (*_eventMouse)(ev);
+ break;
+
+ case Common::EVENT_QUIT:
+ quitGame();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Game tick event
+ if (_eventTick && _eventTick->isValid())
+ (*_eventTick)(ev);
+
+ // Update the screen
+ _graphicsMan->update();
+ _system->updateScreen();
+ _system->delayMillis(50);
+
+ // The event loop may have triggered the quit status. In this case,
+ // stop the execution.
+ if (shouldQuit()) {
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Timer
+///////////////////////////////////////////////////////////////////////////////////
+void LastExpressEngine::soundTimer(void *refCon) {
+ ((LastExpressEngine *)refCon)->handleSoundTimer();
+}
+
+void LastExpressEngine::handleSoundTimer() {
+ if (_frameCounter & 1)
+ if (_soundMan)
+ _soundMan->handleTimer();
+
+ _frameCounter++;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Event Handling
+///////////////////////////////////////////////////////////////////////////////////
+void LastExpressEngine::backupEventHandlers() {
+ _eventMouseBackup = _eventMouse;
+ _eventTickBackup = _eventTick;
+}
+
+void LastExpressEngine::restoreEventHandlers() {
+ if (_eventMouseBackup == NULL || _eventTickBackup == NULL)
+ error("LastExpressEngine::restoreEventHandlers: restore called before backing up the event handlers!");
+
+ _eventMouse = _eventMouseBackup;
+ _eventTick = _eventTickBackup;
+}
+
+void LastExpressEngine::setEventHandlers(EventHandler::EventFunction *mouse, EventHandler::EventFunction *tick) {
+ // Cleanup previous event handlers
+ delete _eventMouse;
+ delete _eventTick;
+
+ _eventMouse = mouse;
+ _eventTick = tick;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Misc Engine
+///////////////////////////////////////////////////////////////////////////////////
+bool LastExpressEngine::hasFeature(EngineFeature f) const {
+ return (f == kSupportsRTL);
+}
+
+void LastExpressEngine::errorString(const char *buf_input, char *buf_output, int buf_output_size) {
+ snprintf(buf_output, (uint)buf_output_size, "%s", buf_input);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/lastexpress.h b/engines/lastexpress/lastexpress.h
new file mode 100644
index 0000000000..a602e4956b
--- /dev/null
+++ b/engines/lastexpress/lastexpress.h
@@ -0,0 +1,153 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_H
+#define LASTEXPRESS_H
+
+#include "lastexpress/debug.h"
+#include "lastexpress/eventhandler.h"
+
+#include "common/random.h"
+#include "common/timer.h"
+
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+
+#include "graphics/pixelformat.h"
+
+/**
+ * This is the namespace of the LastExpress engine.
+ *
+ * Status of this engine:
+ * The game is playable but still very buggy and missing crucial functionality:
+ * - Resources: classes for the resource formats used by the game are mostly
+ * complete (subtitles integration/cursor transparency are missing)
+ * - Display: basic graphic manager functionality is implemented (transitions
+ * and dirty rects handling are missing)
+ * - Menu/Navigation: menu is done and navigation/hotspot handling are also
+ * mostly implemented (with remaining bugs)
+ * - Logic: all the hardcoded AI logic has been implemented, as well as the
+ * shared entity code for drawing/handling of entities.
+ * - Sound: most of the sound queue functionality is still missing
+ * - Savegame: almost all the savegame code is still missing.
+ *
+ * Maintainers:
+ * littleboy, jvprat, clone2727
+ *
+ * Supported games:
+ * - The Last Express
+ */
+namespace LastExpress {
+
+class Cursor;
+class Font;
+class GraphicsManager;
+class Logic;
+class Menu;
+class ResourceManager;
+class SceneManager;
+class SoundManager;
+
+class LastExpressEngine : public Engine {
+protected:
+ // Engine APIs
+ Common::Error run();
+ virtual void errorString(const char *buf_input, char *buf_output, int buf_output_size);
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual Debugger *getDebugger() { return _debugger; }
+
+public:
+ LastExpressEngine(OSystem *syst, const ADGameDescription *gd);
+ ~LastExpressEngine();
+
+ // Misc
+ Common::RandomSource getRandom() const {return _random; }
+
+ // Game
+ Cursor *getCursor() const { return _cursor; }
+ Font *getFont() const { return _font; }
+ Logic *getGameLogic() const { return _logic; }
+ Menu *getGameMenu() const { return _menu; }
+
+ // Managers
+ GraphicsManager *getGraphicsManager() const { return _graphicsMan; }
+ ResourceManager *getResourceManager() const { return _resMan; }
+ SceneManager *getSceneManager() const { return _sceneMan; }
+ SoundManager *getSoundManager() const { return _soundMan; }
+
+ // Event handling
+ bool handleEvents();
+ void pollEvents();
+
+ void backupEventHandlers();
+ void restoreEventHandlers();
+ void setEventHandlers(EventHandler::EventFunction *eventMouse, EventHandler::EventFunction *eventTick);
+
+ bool isDemo() const { return (bool)(_gameDescription->flags & ADGF_DEMO); }
+
+ // Frame Counter
+ uint32 getFrameCounter() { return _frameCounter; }
+ void setFrameCounter(uint32 count) { _frameCounter = count; }
+
+protected:
+ // Sound Timer
+ static void soundTimer(void *ptr);
+ void handleSoundTimer();
+
+private:
+ const ADGameDescription *_gameDescription;
+ Graphics::PixelFormat _pixelFormat;
+
+ // Misc
+ Debugger *_debugger;
+ Common::RandomSource _random;
+
+ // Game
+ Cursor *_cursor;
+ Font *_font;
+ Logic *_logic;
+ Menu *_menu;
+
+ // Frame counter
+ uint32 _frameCounter;
+ uint32 _lastFrameCount;
+
+ // Managers
+ GraphicsManager *_graphicsMan;
+ ResourceManager *_resMan;
+ SceneManager *_sceneMan;
+ SoundManager *_soundMan;
+
+ // Event handlers
+ EventHandler::EventFunction *_eventMouse;
+ EventHandler::EventFunction *_eventTick;
+
+ EventHandler::EventFunction *_eventMouseBackup;
+ EventHandler::EventFunction *_eventTickBackup;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_H
diff --git a/engines/lastexpress/module.mk b/engines/lastexpress/module.mk
new file mode 100644
index 0000000000..12fbb4f85b
--- /dev/null
+++ b/engines/lastexpress/module.mk
@@ -0,0 +1,73 @@
+MODULE := engines/lastexpress
+
+MODULE_OBJS := \
+ data/animation.o \
+ data/archive.o \
+ data/background.o \
+ data/cursor.o \
+ data/font.o \
+ data/scene.o \
+ data/sequence.o \
+ data/snd.o \
+ data/subtitle.o \
+ entities/entity.o \
+ entities/abbot.o \
+ entities/alexei.o \
+ entities/alouan.o \
+ entities/anna.o \
+ entities/august.o \
+ entities/boutarel.o \
+ entities/chapters.o \
+ entities/cooks.o \
+ entities/coudert.o \
+ entities/entity39.o \
+ entities/francois.o \
+ entities/gendarmes.o \
+ entities/hadija.o \
+ entities/ivo.o \
+ entities/kahina.o \
+ entities/kronos.o \
+ entities/mahmud.o \
+ entities/max.o \
+ entities/mertens.o \
+ entities/milos.o \
+ entities/mmeboutarel.o \
+ entities/pascale.o \
+ entities/rebecca.o \
+ entities/salko.o \
+ entities/servers0.o \
+ entities/servers1.o \
+ entities/sophie.o \
+ entities/tables.o \
+ entities/tatiana.o \
+ entities/train.o \
+ entities/vassili.o \
+ entities/verges.o \
+ entities/vesna.o \
+ entities/yasmin.o \
+ game/action.o \
+ game/beetle.o \
+ game/entities.o \
+ game/fight.o \
+ game/inventory.o \
+ game/logic.o \
+ game/menu.o \
+ game/object.o \
+ game/savegame.o \
+ game/savepoint.o \
+ game/scenes.o \
+ game/sound.o \
+ game/state.o \
+ debug.o \
+ detection.o \
+ graphics.o \
+ lastexpress.o \
+ resource.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_LASTEXPRESS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk \ No newline at end of file
diff --git a/engines/lastexpress/resource.cpp b/engines/lastexpress/resource.cpp
new file mode 100644
index 0000000000..5a77b23602
--- /dev/null
+++ b/engines/lastexpress/resource.cpp
@@ -0,0 +1,249 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/resource.h"
+
+#include "lastexpress/data/background.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/debug.h"
+#include "lastexpress/helpers.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+
+namespace LastExpress {
+
+const char *archiveDemoPath = "demo.hpf";
+const char *archiveHDPath = "hd.hpf";
+const char *archiveCD1Path = "cd1.hpf";
+const char *archiveCD2Path = "cd2.hpf";
+const char *archiveCD3Path = "cd3.hpf";
+
+ResourceManager::ResourceManager(bool isDemo) : _isDemo(isDemo) {
+}
+
+ResourceManager::~ResourceManager() {
+ reset();
+}
+
+bool ResourceManager::isArchivePresent(ArchiveIndex type) {
+ switch (type) {
+ default:
+ case kArchiveAll:
+ error("ResourceManager::isArchivePresent: Only checks for single CDs are valid!");
+
+ case kArchiveCd1:
+ return Common::File::exists(archiveCD1Path);
+
+ case kArchiveCd2:
+ return Common::File::exists(archiveCD2Path);
+
+ case kArchiveCd3:
+ return Common::File::exists(archiveCD3Path);
+ }
+}
+
+// Load a specific archive collection
+// - type is ignored in the demo version
+// - use ArchiveAll to load all three cds
+// - HD.hpf is always loaded along with the selected archive(s)
+// - will remove all other archives
+bool ResourceManager::loadArchive(ArchiveIndex type) {
+ // Unload all archives
+ reset();
+
+ // Demo version
+ if (_isDemo)
+ return loadArchive(archiveDemoPath);
+
+ // Load HD
+ if (!loadArchive(archiveHDPath))
+ return false;
+
+ switch(type) {
+ case kArchiveCd1:
+ return loadArchive(archiveCD1Path);
+
+ case kArchiveCd2:
+ return loadArchive(archiveCD2Path);
+
+ case kArchiveCd3:
+ return loadArchive(archiveCD3Path);
+
+ case kArchiveAll:
+ default:
+ if (loadArchive(archiveCD1Path))
+ if (loadArchive(archiveCD2Path))
+ return loadArchive(archiveCD3Path);
+ break;
+ }
+
+ return false;
+}
+
+void ResourceManager::reset() {
+ // Free the loaded archives
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it)
+ SAFE_DELETE(*it);
+
+ _archives.clear();
+}
+
+bool ResourceManager::loadArchive(const Common::String &name) {
+ HPFArchive *archive = new HPFArchive(name);
+
+ if (archive->count() == 0) {
+ debugC(2, kLastExpressDebugResource, "Error opening archive: %s", name.c_str());
+
+ delete archive;
+
+ return false;
+ }
+
+ _archives.push_back(archive);
+
+ return true;
+}
+
+// Get a stream to file in the archive
+// - same as createReadStreamForMember except it checks if the file exists and will assert / output a debug message if not
+Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) {
+
+ // Check if the file exits in the archive
+ if (!hasFile(name)) {
+//#ifdef _DEBUG
+// error("ResourceManager::getFileStream: cannot open file: %s", name.c_str());
+//#endif
+ debugC(2, kLastExpressDebugResource, "Error opening file: %s", name.c_str());
+ return NULL;
+ }
+
+ debugC(2, kLastExpressDebugResource, "Opening file: %s", name.c_str());
+
+ return createReadStreamForMember(name);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Archive functions
+//////////////////////////////////////////////////////////////////////////
+bool ResourceManager::hasFile(const Common::String &name) {
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+ if ((*it)->hasFile(name))
+ return true;
+ }
+
+ return false;
+}
+
+int ResourceManager::listMembers(Common::ArchiveMemberList &list) {
+ int count = 0;
+
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+
+ Common::ArchiveMemberList members;
+ count += (*it)->listMembers(members);
+
+ list.insert(list.end(), members.begin(), members.end());
+ }
+
+ return count;
+}
+
+Common::ArchiveMemberPtr ResourceManager::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *ResourceManager::createReadStreamForMember(const Common::String &name) const {
+ for (Common::Array<HPFArchive *>::const_iterator it = _archives.begin(); it != _archives.end(); ++it) {
+
+ Common::SeekableReadStream *stream = (*it)->createReadStreamForMember(name);
+
+ if (stream)
+ return stream;
+ }
+
+ return NULL;
+}
+
+
+// Resource loading
+
+Background *ResourceManager::loadBackground(const Common::String &name) const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember(name + ".bg");
+ if (!stream)
+ return NULL;
+
+ // Create the new background & load the data
+ Background *bg = new Background();
+ if (!bg->load(stream)) {
+ delete bg;
+ // stream should be freed by the Background instance
+ return NULL;
+ }
+
+ return bg;
+}
+
+Cursor *ResourceManager::loadCursor() const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember("cursors.tbm");
+ if (!stream)
+ return NULL;
+
+ // Create the new background
+ Cursor *c = new Cursor();
+ if (!c->load(stream)) {
+ delete c;
+ // stream should be freed by the Cursor instance
+ return NULL;
+ }
+
+ return c;
+}
+
+Font *ResourceManager::loadFont() const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember("font.dat");
+ if (!stream)
+ return NULL;
+
+ // Create the new background
+ Font *f = new Font();
+ if (!f->load(stream)) {
+ delete f;
+ // stream should be freed by the Font instance
+ return NULL;
+ }
+
+ return f;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/resource.h b/engines/lastexpress/resource.h
new file mode 100644
index 0000000000..ea6508edc3
--- /dev/null
+++ b/engines/lastexpress/resource.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_RESOURCE_H
+#define LASTEXPRESS_RESOURCE_H
+
+#include "lastexpress/data/archive.h"
+#include "lastexpress/shared.h"
+
+namespace LastExpress {
+
+class Background;
+class Cursor;
+class Font;
+
+class ResourceManager : public Common::Archive {
+public:
+ ResourceManager(bool demo);
+ ~ResourceManager();
+
+ // Loading
+ bool loadArchive(ArchiveIndex type);
+ static bool isArchivePresent(ArchiveIndex type);
+ Common::SeekableReadStream *getFileStream(const Common::String &name);
+
+ // Archive functions
+ bool hasFile(const Common::String &name);
+ int listMembers(Common::ArchiveMemberList &list);
+ Common::ArchiveMemberPtr getMember(const Common::String &name);
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+ // Resource loading
+ Background *loadBackground(const Common::String &name) const;
+ Cursor *loadCursor() const;
+ Font *loadFont() const;
+
+private:
+ bool _isDemo;
+
+ bool loadArchive(const Common::String &name);
+ void reset();
+
+ Common::Array<HPFArchive *> _archives;
+
+ friend class Debugger;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_RESOURCE_H
diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h
new file mode 100644
index 0000000000..80e227e6a7
--- /dev/null
+++ b/engines/lastexpress/shared.h
@@ -0,0 +1,1716 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SHARED_H
+#define LASTEXPRESS_SHARED_H
+
+#include "common/func.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Time values
+//////////////////////////////////////////////////////////////////////////
+
+// Time is measured in ticks, with 15 ticks per second. One minute is 900
+// ticks, one hour is 54,000 ticks, and one day is 1,296,000 ticks.
+
+enum TimeValue {
+ kTimeNone = 0,
+ kTime5933 = 5933,
+
+ kTimeCityParis = 1037700, // Day 1, 19:13
+ kTime1039500 = 1039500, // Day 1, 19:15
+ kTimeStartGame = 1061100, // Day 1, 19:39
+
+ // Chapter 1
+ kTimeChapter1 = 1062000, // Day 1, 19:40
+ kTime1071000 = 1071000, // Day 1, 19:50
+ kTimeParisEpernay = 1075500, // Day 1, 19:55
+ kTime1080000 = 1080000, // Day 1, 20:00
+ kTime1084500 = 1084500, // Day 1, 20:05
+ kTime1089000 = 1089000, // Day 1, 20:10
+ kTime1093500 = 1093500, // Day 1, 20:15
+ kTime1094400 = 1094400, // Day 1, 20:16
+ kTime1096200 = 1096200, // Day 1, 20:18
+ kTime1098000 = 1098000, // Day 1, 20:20
+ kTime1102500 = 1102500, // Day 1, 20:25
+ kTime1107000 = 1107000, // Day 1, 20:30
+ kTime1111500 = 1111500, // Day 1, 20:35
+ kTime1120500 = 1120500, // Day 1, 20:45
+ kTime1125000 = 1125000, // Day 1, 20:50
+ kTime1134000 = 1134000, // Day 1, 21:00
+ kTime1138500 = 1138500, // Day 1, 21:05
+ kTime1143000 = 1143000, // Day 1, 21:10
+ kTimeEnterEpernay = 1147500, // Day 1, 21:15
+ kTimeCityEpernay = 1148400, // Day 1, 21:16
+ kTimeExitEpernay = 1150200, // Day 1, 21:18
+ kTime1156500 = 1156500, // Day 1, 21:25
+ kTime1161000 = 1161000, // Day 1, 21:30
+ kTime1162800 = 1162800, // Day 1, 21:32
+ kTime1165500 = 1165500, // Day 1, 21:35
+ kTime1167300 = 1167300, // Day 1, 21:37
+ kTimeEnterChalons = 1170000, // Day 1, 21:40
+ kTimeCityChalons = 1170900, // Day 1, 21:41
+ kTimeExitChalons = 1173600, // Day 1, 21:44
+ kTime1174500 = 1174500, // Day 1, 21:45
+ kTime1179000 = 1179000, // Day 1, 21:50
+ kTime1183500 = 1183500, // Day 1, 21:55
+ kTime1184400 = 1184400, // Day 1, 21:56
+ kTime1188000 = 1188000, // Day 1, 22:00
+ kTime1189800 = 1189800, // Day 1, 22:02
+ kTime1192500 = 1192500, // Day 1, 22:05
+ kTime1197000 = 1197000, // Day 1, 22:10
+ kTime1201500 = 1201500, // Day 1, 22:15
+ kTime1206000 = 1206000, // Day 1, 22:20
+ kTime1215000 = 1215000, // Day 1, 22:30
+ kTime1224000 = 1224000, // Day 1, 22:40
+ kTime1225800 = 1225800, // Day 1, 22:42
+ kTimeCityBarLeDuc = 1228500, // Day 1, 22:45
+ kTimeExitBarLeDuc = 1231200, // Day 1, 22:48
+ kTime1233000 = 1233000, // Day 1, 22:50
+ kTime1242000 = 1242000, // Day 1, 23:00
+ kTime1260000 = 1260000, // Day 1, 23:20
+ kTimeCityNancy = 1303200, // Day 2, 00:08
+ kTimeExitNancy = 1307700, // Day 2, 00:13
+ kTime1323000 = 1323000, // Day 2, 00:30
+ kTimeCityLuneville = 1335600, // Day 2, 00:44
+ kTimeExitLuneville = 1338300, // Day 2, 00:47
+ kTimeCityAvricourt = 1359900, // Day 2, 01:11
+ kTimeExitAvricourt = 1363500, // Day 2, 01:15
+ kTimeCityDeutschAvricourt = 1367100, // Day 2, 01:19
+ kTimeExitDeutschAvricourt = 1370700, // Day 2, 01:23
+ kTime1386000 = 1386000, // Day 2, 01:40
+ kTimeBedTime = 1404000, // Day 2, 02:00
+ kTime1417500 = 1417500, // Day 2, 02:15
+ kTimeEnterStrasbourg = 1424700, // Day 2, 02:23
+ kTime1449000 = 1449000, // Day 2, 02:50
+ kTime1458000 = 1458000, // Day 2, 03:00
+ kTime1485000 = 1485000, // Day 2, 03:30
+ kTime1489500 = 1489500, // Day 2, 03:35
+ kTimeCityStrasbourg = 1490400, // Day 2, 03:36
+ kTime1492200 = 1492200, // Day 2, 03:38
+ kTimeExitStrasbourg = 1493100, // Day 2, 03:39
+ kTimeChapter1End = 1494000, // Day 2, 03:40
+ kTime1503000 = 1503000, // Day 2, 03:50
+ kTime1512000 = 1512000, // Day 2, 04:00
+ kTimeCityBadenOos = 1539000, // Day 2, 04:30
+ kTimeExitBadenOos = 1541700, // Day 2, 04:33
+ kTimeCityKarlsruhe = 1563300, // Day 2, 04:57
+ kTimeCityStuttgart = 1656000, // Day 2, 06:40
+ kTimeChapter1End2 = 1647000, // Day 2, 06:30
+ kTimeChapter1End3 = 1674000, // Day 2, 07:00
+ kTimeCityGeislingen = 1713600, // Day 2, 07:44
+ kTime1714500 = 1714500, // Day 2, 07:45
+ kTimeCityUlm = 1739700, // Day 2, 08:13
+
+ // Chapter 2
+ kTimeChapter2 = 1750500, // Day 2, 08:25
+ kTime1759500 = 1759500, // Day 2, 08:35
+ kTime1755000 = 1755000, // Day 2, 08:30
+ kTime1764000 = 1764000, // Day 2, 08:40
+ kTime1768500 = 1768500, // Day 2, 08:45
+ kTime1773000 = 1773000, // Day 2, 08:50
+ kTime1777500 = 1777500, // Day 2, 08:55
+ kTime1782000 = 1782000, // Day 2, 09:00
+ kTime1786500 = 1786500, // Day 2, 09:05
+ kTime1791000 = 1791000, // Day 2, 09:10
+ kTime1800000 = 1800000, // Day 2, 09:20
+ kTime1801800 = 1801800, // Day 2, 09:22
+ kTime1806300 = 1806300, // Day 2, 09:27
+ kTime1809000 = 1809000, // Day 2, 09:30
+ kTimeCityAugsburg = 1809900, // Day 2, 09:31
+ kTime1813500 = 1813500, // Day 2, 09:35
+ kTime1818000 = 1818000, // Day 2, 09:40
+ kTime1818900 = 1818900, // Day 2, 09:41
+ kTime1820700 = 1820700, // Day 2, 09:43
+ kTime1822500 = 1822500, // Day 2, 09:45
+ kTime1827000 = 1827000, // Day 2, 09:50
+ kTime1831500 = 1831500, // Day 2, 09:55
+ kTime1836000 = 1836000, // Day 2, 10:00
+ kTime1845000 = 1845000, // Day 2, 10:10
+ kTime1849500 = 1849500, // Day 2, 10:15
+ kTimeCityMunich = 1852200, // Day 2, 10:18
+
+ // Chapter 3
+ kTimeChapter3 = 1944000, // Day 2, 12:00
+ kTime1953000 = 1953000, // Day 2, 12:10
+ kTime1966500 = 1966500, // Day 2, 12:25
+ kTime1969200 = 1969200, // Day 2, 12:28
+ kTime1971000 = 1971000, // Day 2, 12:30
+ kTimeEnterSalzbourg = 1982700, // Day 2, 12:43
+ kTime1983600 = 1983600, // Day 2, 12:44
+ kTimeCitySalzbourg = 1984500, // Day 2, 12:45
+ kTime1989000 = 1989000, // Day 2, 12:50
+ kTimeExitSalzbourg = 1989900, // Day 2, 12:51
+ kTime1993500 = 1993500, // Day 2, 12:55
+ kTime1998000 = 1998000, // Day 2, 13:00
+ kTime2002500 = 2002500, // Day 2, 13:05
+ kTime2011500 = 2011500, // Day 2, 13:15
+ kTime2016000 = 2016000, // Day 2, 13:20
+ kTime2020500 = 2020500, // Day 2, 13:25
+ kTime2025000 = 2025000, // Day 2, 13:30
+ kTime2034000 = 2034000, // Day 2, 13:40
+ kTime2038500 = 2038500, // Day 2, 13:45
+ kTime2040300 = 2040300, // Day 2, 13:47
+ kTime2043000 = 2043000, // Day 2, 13:50
+ kTimeEnterAttnangPuchheim = 2047500, // Day 2, 13:55
+ kTimeCityAttnangPuchheim = 2049300, // Day 2, 13:57
+ kTime2052000 = 2052000, // Day 2, 14:00
+ kTimeExitAttnangPuchheim = 2052900, // Day 2, 14:01
+ kTime2056500 = 2056500, // Day 2, 14:05
+ kTime2061000 = 2061000, // Day 2, 14:10
+ kTime2062800 = 2062800, // Day 2, 14:12
+ kTime2065500 = 2065500, // Day 2, 14:15
+ kTime2070000 = 2070000, // Day 2, 14:20
+ kTimeEnterWels = 2073600, // Day 2, 14:24
+ kTimeCityWels = 2075400, // Day 2, 14:26
+ kTime2079000 = 2079000, // Day 2, 14:30
+ kTimeExitWels = 2079900, // Day 2, 14:31
+ kTime2083500 = 2083500, // Day 2, 14:35
+ kTime2088000 = 2088000, // Day 2, 14:40
+ kTime2088900 = 2088900, // Day 2, 14:41
+ kTime2092500 = 2092500, // Day 2, 14:45
+ kTime2097000 = 2097000, // Day 2, 14:50
+ kTimeEnterLinz = 2099700, // Day 2, 14:53
+ kTimeCityLinz = 2101500, // Day 2, 14:55
+ kTime2106000 = 2106000, // Day 2, 15:00
+ kTime2110500 = 2110500, // Day 2, 15:05
+ kTime2115000 = 2115000, // Day 2, 15:10
+ kTime2117700 = 2117700, // Day 2, 15:13
+ kTime2119500 = 2119500, // Day 2, 15:15
+ kTime2124000 = 2124000, // Day 2, 15:20
+ kTime2133000 = 2133000, // Day 2, 15:30
+ kTime2138400 = 2138400, // Day 2, 15:36
+ kTime2142000 = 2142000, // Day 2, 15:40
+ kTime2146500 = 2146500, // Day 2, 15:45
+ kTime2147400 = 2147400, // Day 2, 15:46
+ kTime2151000 = 2151000, // Day 2, 15:50
+ kTimeCityAmstetten = 2154600, // Day 2, 15:54
+ kTime2155500 = 2155500, // Day 2, 15:55
+ kTime2160000 = 2160000, // Day 2, 16:00
+ kTime2169000 = 2169000, // Day 2, 16:10
+ kTime2173500 = 2173500, // Day 2, 16:15
+ kTime2187000 = 2187000, // Day 2, 16:30
+ kTime2182500 = 2182500, // Day 2, 16:25
+ kTime2196000 = 2196000, // Day 2, 16:40
+ kTime2200500 = 2200500, // Day 2, 16:45
+ kTime2205000 = 2205000, // Day 2, 16:50
+ kTime2214000 = 2214000, // Day 2, 17:00
+ kTime2218500 = 2218500, // Day 2, 17:05
+ kTime2223000 = 2223000, // Day 2, 17:10
+ kTime2227500 = 2227500, // Day 2, 17:15
+ kTime2241000 = 2241000, // Day 2, 17:30
+ kTime2248200 = 2248200, // Day 2, 17:38
+ kTime2250000 = 2250000, // Day 2, 17:40
+ kTime2254500 = 2254500, // Day 2, 17:45
+ kTime2259000 = 2259000, // Day 2, 17:50
+ kTime2263500 = 2263500, // Day 2, 17:55
+ kTime2266200 = 2266200, // Day 2, 17:58
+ kTimeCityVienna = 2268000, // Day 2, 18:00
+
+ // Chapter 4
+ kTime2349000 = 2349000, // Day 2, 19:30
+ kTimeChapter4 = 2353500, // Day 2, 19:35
+ kTime2354400 = 2354400, // Day 2, 19:36
+ kTime2356200 = 2356200, // Day 2, 19:38
+ kTime2358000 = 2358000, // Day 2, 19:40
+ kTime2360700 = 2360700, // Day 2, 19:43
+ kTime2362500 = 2362500, // Day 2, 19:45
+ kTime2361600 = 2361600, // Day 2, 19:44
+ kTime2367000 = 2367000, // Day 2, 19:50
+ kTime2370600 = 2370600, // Day 2, 19:54
+ kTime2378700 = 2378700, // Day 2, 20:03
+ kTimeEnterPoszony = 2381400, // Day 2, 20:06
+ kTimeCityPoszony = 2383200, // Day 2, 20:08
+ kTime2385000 = 2385000, // Day 2, 20:10
+ kTimeExitPoszony = 2386800, // Day 2, 20:12
+ kTime2389500 = 2389500, // Day 2, 20:15
+ kTime2394000 = 2394000, // Day 2, 20:20
+ kTime2398500 = 2398500, // Day 2, 20:25
+ kTime2403000 = 2403000, // Day 2, 20:30
+ kTime2407500 = 2407500, // Day 2, 20:35
+ kTime2410200 = 2410200, // Day 2, 20:38
+ kTime2412000 = 2412000, // Day 2, 20:40
+ kTime2414700 = 2414700, // Day 2, 20:43
+ kTime2415600 = 2415600, // Day 2, 20:44
+ kTimeEnterGalanta = 2416500, // Day 2, 20:45
+ kTimeCityGalanta = 2418300, // Day 2, 20:47
+ kTime2421000 = 2421000, // Day 2, 20:50
+ kTimeExitGalanta = 2421900, // Day 2, 20:51
+ kTime2422800 = 2422800, // Day 2, 20:52
+ kTime2428200 = 2428200, // Day 2, 20:58
+ kTime2425500 = 2425500, // Day 2, 20:55
+ kTime2430000 = 2430000, // Day 2, 21:00
+ kTime2434500 = 2434500, // Day 2, 21:05
+ kTime2439000 = 2439000, // Day 2, 21:10
+ kTime2443500 = 2443500, // Day 2, 21:15
+ kTime2448000 = 2448000, // Day 2, 21:20
+ kTime2452500 = 2452500, // Day 2, 21:25
+ kTime2455200 = 2455200, // Day 2, 21:28
+ kTime2457000 = 2457000, // Day 2, 21:30
+ kTime2466000 = 2466000, // Day 2, 21:40
+ kTime2470500 = 2470500, // Day 2, 21:45
+ kTime2475000 = 2475000, // Day 2, 21:50
+ kTime2479500 = 2479500, // Day 2, 21:55
+ kTime2484000 = 2484000, // Day 2, 22:00
+ kTime2488500 = 2488500, // Day 2, 22:05
+ kTime2493000 = 2493000, // Day 2, 22:10
+ kTime2506500 = 2506500, // Day 2, 22:25
+ kTime2507400 = 2507400, // Day 2, 22:26
+ kTime2511000 = 2511000, // Day 2, 22:30
+ kTime2511900 = 2511900, // Day 2, 22:31
+ kTime2517300 = 2517300, // Day 2, 22:37
+ kTime2519100 = 2519100, // Day 2, 22:39
+ kTime2520000 = 2520000, // Day 2, 22:40
+ kTime2533500 = 2533500, // Day 2, 22:55
+ kTime2535300 = 2535300, // Day 2, 22:57
+ kTime2538000 = 2538000, // Day 2, 23:00
+ kTimeCityBudapest = 2551500, // Day 2, 23:15
+
+ // Chapter 5
+ kTimeChapter5 = 2844000, // Day 3, 04:40
+ kTimeTrainStopped = 2898000, // Day 3, 05:40
+ kTime2907000 = 2907000, // Day 3, 05:50
+ kTime2916000 = 2916000, // Day 3, 06:00
+ kTimeCityBelgrade = 2952000, // Day 3, 06:40
+ kTimeTrainStopped2 = 2943000, // Day 3, 06:30
+ kTime2983500 = 2983500, // Day 3, 07:15
+ kTimeCityNish = 3205800, // Day 3, 11:22
+ kTimeCityTzaribrod = 3492000, // Day 3, 16:40
+ kTime3645000 = 3645000, // Day 3, 19:30
+ kTimeCitySofia = 3690000, // Day 3, 20:20
+ kTimeCityAdrianople = 4320900, // Day 4, 08:01
+ kTime4923000 = 4923000, // Day 4, 19:10
+ kTime4929300 = 4929300, // Day 4, 19:17
+ kTimeCityConstantinople = 4941000, // Day 4, 19:30
+
+
+ kTime10881000 = 10881000,
+ kTimeEnd = 15803100,
+ kTime16451100 = 16451100,
+
+ kTimeInvalid = 2147483647,
+ kTimeInvalid2 = 0xFFFFFEDA
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Archive & Chapter ID
+//////////////////////////////////////////////////////////////////////////
+enum ArchiveIndex {
+ kArchiveAll = 0,
+ kArchiveCd1 = 1,
+ kArchiveCd2 = 2,
+ kArchiveCd3 = 3
+};
+
+enum ChapterIndex {
+ kChapterAll = 0,
+ kChapter1 = 1,
+ kChapter2 = 2,
+ kChapter3 = 3,
+ kChapter4 = 4,
+ kChapter5 = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Index of scenes
+//////////////////////////////////////////////////////////////////////////
+enum SceneIndex {
+ kSceneNone = 0,
+ kSceneMenu = 1,
+
+ kSceneIntro = 30,
+
+ // Inventory
+ kSceneMatchbox = 31,
+ kSceneTelegram = 32,
+ kScenePassengerList = 33,
+ kSceneScarf = 34,
+ kSceneParchemin = 35,
+ kSceneArticle = 36,
+ kScenePaper = 37,
+ kSceneFirebird = 38,
+ kSceneBriefcase = 39,
+
+ // Normal scenes
+ kSceneDefault = 40,
+ kScene41 = 41,
+ kSceneCompartmentCorpse = 42, // Tyler compartment with corpse on floor
+
+ // Fight
+ kSceneFightMilos = 43,
+ kSceneFightMilosBedOpened = 44,
+ kSceneFightAnna = 45,
+ kSceneFightIvo = 46,
+ kSceneFightSalko = 47,
+ kSceneFightVesna = 48,
+
+ kSceneEuropeMap = 49,
+
+ // Game over
+ kSceneGameOverStopPolice = 50,
+ kSceneGameOverTrainStopped = 51,
+ kSceneGameOverTrainStopped2 = 52,
+ kSceneGameOverTrainExplosion = 53,
+ kSceneGameOverTrainExplosion2 = 54,
+ kSceneGameOverBloodJacket = 55,
+ kSceneGameOverPolice = 56,
+ kSceneGameOverPolice1 = 57,
+ kSceneGameOverAnnaDied = 58,
+ kSceneGameOverVienna = 59,
+ kSceneGameOverVienna1 = 60,
+ kSceneGameOverVienna2 = 61,
+ kSceneGameOverAlarm = 62,
+ kSceneGameOverPolice2 = 63,
+ kSceneGameOverAlarm2 = 64,
+
+ // Start screen
+ kSceneStartScreen = 65,
+
+ kSceneBeetle = 128,
+
+ kSceneFightDefault = 820,
+
+ kSceneInvalid = 0xffffffff
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Jacket
+//////////////////////////////////////////////////////////////////////////
+enum JacketType {
+ kJacketOriginal = 0,
+ kJacketBlood = 1,
+ kJacketGreen = 2
+};
+
+//////////////////////////////////////////////////////////////////////////
+// City
+//////////////////////////////////////////////////////////////////////////
+enum CityIndex {
+ kCityEpernay = 0,
+ kCityChalons,
+ kCityBarleduc,
+ kCityNancy,
+ kCityLuneville,
+ kCityAvricourt, // 5
+ kCityDeutschAvricourt,
+ kCityStrasbourg,
+ kCityBadenOos,
+ kCitySalzbourg,
+ kCityAttnangPuchheim, // 10
+ kCityWels,
+ kCityLinz,
+ kCityVienna,
+ kCityPoszony,
+ kCityGalanta, // 15
+ kCityPolice
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame ID
+//////////////////////////////////////////////////////////////////////////
+enum GameId {
+ kGameBlue,
+ kGameRed,
+ kGameGreen,
+ kGamePurple,
+ kGameTeal,
+ kGameGold
+};
+
+enum SavegameType {
+ kSavegameTypeIndex = 0,
+ kSavegameTypeTime = 1,
+ kSavegameTypeEvent = 2,
+ kSavegameTypeEvent2 = 3,
+ kSavegameTypeAuto = 4,
+ kSavegameTypeTickInterval = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Cursor style
+//////////////////////////////////////////////////////////////////////////
+enum CursorStyle {
+ kCursorNormal,
+ kCursorForward,
+ kCursorBackward,
+ kCursorTurnRight,
+ kCursorTurnLeft,
+ kCursorUp,
+ kCursorDown,
+ kCursorLeft,
+ kCursorRight,
+ kCursorHand,
+ kCursorHandKnock, // 10
+ kCursorMagnifier,
+ kCursorHandPointer,
+ kCursorSleep,
+ kCursorTalk,
+ kCursorTalk2, // Need better name
+
+ // Items
+ kCursorMatchBox,
+ kCursorTelegram,
+ kCursorPassengerList,
+ kCursorArticle,
+ kCursorScarf, // 20
+ kCursorPaper,
+ kCursorParchemin,
+ kCursorMatch,
+ kCursorWhistle,
+ kCursorKey,
+ kCursorBomb,
+ kCursorFirebird,
+ kCursorBriefcase,
+ kCursorCorpse,
+
+ // Combat
+ kCursorPunchLeft, // 30
+ kCursorPunchRight,
+
+ // Portraits
+ kCursorPortrait, // 32
+ kCursorPortraitSelected,
+ kCursorPortraitGreen,
+ kCursorPortraitGreenSelected,
+ kCursorPortraitYellow,
+ kCursorPortraitYellowSelected,
+ kCursorHourGlass,
+ kCursorEggBlue,
+ kCursorEggRed, // 40
+ kCursorEggGreen,
+ kCursorEggPurple,
+ kCursorEggTeal,
+ kCursorEggGold,
+ kCursorEggClock,
+ kCursorNormal2,
+ kCursorBlank,
+ kCursorMAX,
+
+ // Special
+ kCursorProcess = 128,
+ kCursorKeepValue = 255
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Position - should be between 0 & 100
+//////////////////////////////////////////////////////////////////////////
+typedef unsigned char Position;
+
+//////////////////////////////////////////////////////////////////////////
+// EntityPosition
+//////////////////////////////////////////////////////////////////////////
+enum EntityPosition {
+ kPositionNone = 0,
+ kPosition_1 = 1,
+ kPosition_3 = 3,
+ kPosition_4 = 4,
+ kPosition_500 = 500,
+ kPosition_540 = 540,
+ kPosition_750 = 750,
+ kPosition_849 = 849,
+ kPosition_850 = 850,
+ kPosition_851 = 851,
+ kPosition_1200 = 1200,
+ kPosition_1430 = 1430,
+ kPosition_1500 = 1500,
+ kPosition_1540 = 1540,
+ kPosition_1750 = 1750,
+ kPosition_2000 = 2000,
+ kPosition_2087 = 2087,
+ kPosition_2086 = 2086,
+ kPosition_2088 = 2088,
+ kPosition_2110 = 2110,
+ kPosition_2300 = 2300,
+ kPosition_2330 = 2330,
+ kPosition_2410 = 2410,
+ kPosition_2436 = 2436,
+ kPosition_2490 = 2490,
+ kPosition_2500 = 2500,
+ kPosition_2587 = 2587,
+ kPosition_2588 = 2588,
+ kPosition_2690 = 2690,
+ kPosition_2740 = 2740,
+ kPosition_2830 = 2830,
+ kPosition_2980 = 2980,
+ kPosition_3050 = 3050,
+ kPosition_3110 = 3110,
+ kPosition_3390 = 3390,
+ kPosition_3450 = 3450,
+ kPosition_3500 = 3500,
+ kPosition_3550 = 3550,
+ kPosition_3650 = 3650,
+ kPosition_3760 = 3760,
+ kPosition_3820 = 3820,
+ kPosition_3890 = 3890,
+ kPosition_3969 = 3969,
+ kPosition_3970 = 3970,
+ kPosition_4070 = 4070,
+ kPosition_4100 = 4100,
+ kPosition_4370 = 4370,
+ kPosition_4455 = 4455,
+ kPosition_4460 = 4460,
+ kPosition_4500 = 4500,
+ kPosition_4590 = 4590,
+ kPosition_4680 = 4680,
+ kPosition_4689 = 4689,
+ kPosition_4690 = 4690,
+ kPosition_4691 = 4691,
+ kPosition_4770 = 4470,
+ kPosition_4840 = 4840,
+ kPosition_5000 = 5000,
+ kPosition_5090 = 5090,
+ kPosition_5140 = 5140,
+ kPosition_5419 = 5419,
+ kPosition_5420 = 5420,
+ kPosition_5440 = 5440,
+ kPosition_5500 = 5500,
+ kPosition_5540 = 5540,
+ kPosition_5610 = 5610,
+ kPosition_5790 = 5790,
+ kPosition_5799 = 5799,
+ kPosition_5800 = 5800,
+ kPosition_5810 = 5810,
+ kPosition_5890 = 5890,
+ kPosition_5900 = 5900,
+ kPosition_5970 = 5970,
+ kPosition_6000 = 6000,
+ kPosition_6130 = 6130,
+ kPosition_6160 = 6160,
+ kPosition_6220 = 6220,
+ kPosition_6410 = 6410,
+ kPosition_6460 = 6460,
+ kPosition_6469 = 6469,
+ kPosition_6470 = 6470,
+ kPosition_6471 = 6471,
+ kPosition_6800 = 6800,
+ kPosition_6850 = 6850,
+ kPosition_7000 = 7000,
+ kPosition_7160 = 7160,
+ kPosition_7250 = 7250,
+ kPosition_7320 = 7320,
+ kPosition_7500 = 7500,
+ kPosition_7510 = 7510,
+ kPosition_7850 = 7850,
+ kPosition_7870 = 7870,
+ kPosition_7900 = 7900,
+ kPosition_7950 = 7950,
+ kPosition_8000 = 8000,
+ kPosition_8012 = 8012,
+ kPosition_8013 = 8013,
+ kPosition_8160 = 8160,
+ kPosition_8200 = 8200,
+ kPosition_8500 = 8500,
+ kPosition_8512 = 8512,
+ kPosition_8513 = 8513,
+ kPosition_8514 = 8514,
+ kPosition_8800 = 8800,
+ kPosition_9020 = 9020,
+ kPosition_9269 = 9269,
+ kPosition_9250 = 9250,
+ kPosition_9270 = 9270,
+ kPosition_9271 = 9271,
+ kPosition_9460 = 9460,
+ kPosition_9500 = 9500,
+ kPosition_9510 = 9510,
+ kPosition_30000 = 30000
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Location
+//////////////////////////////////////////////////////////////////////////
+enum Location {
+ kLocationOutsideCompartment = 0,
+ kLocationInsideCompartment = 1,
+ kLocationOutsideTrain = 2
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Car
+//////////////////////////////////////////////////////////////////////////
+enum CarIndex {
+ kCarNone = 0,
+ kCarBaggageRear = 1,
+ kCarKronos = 2,
+ kCarGreenSleeping = 3,
+ kCarRedSleeping = 4,
+ kCarRestaurant = 5,
+ kCarBaggage = 6,
+ kCarCoalTender = 7,
+ kCarLocomotive = 8,
+ kCar9 = 9
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Clothes
+//////////////////////////////////////////////////////////////////////////
+enum ClothesIndex {
+ kClothesDefault = 0,
+ kClothes1 = 1,
+ kClothes2 = 2,
+ kClothes3 = 3,
+
+ kClothesInvalid
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Location of objects
+//////////////////////////////////////////////////////////////////////////
+enum ObjectLocation {
+ kObjectLocationNone = 0,
+ kObjectLocation1 = 1, // Floor?
+ kObjectLocation2 = 2, // Bed ?
+ kObjectLocation3 = 3,
+ kObjectLocation4 = 4, // Window ?
+ kObjectLocation5 = 5,
+ kObjectLocation6 = 6,
+ kObjectLocation7 = 7,
+ kObjectLocation8 = 8,
+ kObjectLocation9 = 9,
+ kObjectLocation10 = 10,
+ kObjectLocation18 = 18
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Entity direction
+//////////////////////////////////////////////////////////////////////////
+enum EntityDirection {
+ kDirectionNone = 0,
+ kDirectionUp = 1,
+ kDirectionDown = 2,
+ kDirectionLeft = 3,
+ kDirectionRight = 4,
+ kDirectionSwitch = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Combat
+//////////////////////////////////////////////////////////////////////////
+enum FightType {
+ kFightMilos = 2001,
+ kFightAnna = 2002,
+ kFightIvo = 2003,
+ kFightSalko = 2004,
+ kFightVesna = 2005
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Index of items in inventory data
+//////////////////////////////////////////////////////////////////////////
+enum InventoryItem {
+ kItemNone,
+ kItemMatchBox,
+ kItem2,
+ kItem3,
+ kItemTelegram,
+ kItem5, // 5
+ kItemPassengerList,
+ kItem7,
+ kItemScarf,
+ kItem9,
+ kItemParchemin, // 10
+ kItem11,
+ kItemMatch,
+ kItemWhistle,
+ kItemBeetle,
+ kItemKey, // 15
+ kItemBomb,
+ kItem17,
+ kItemFirebird,
+ kItemBriefcase,
+ kItemCorpse, // 20
+ kItemGreenJacket,
+ kItem22,
+ kItemPaper,
+ kItemArticle,
+ kItem25, // 25
+ kItem26,
+ kItem27,
+ kItem28,
+ kItem29,
+ kItem30, // 30
+ kItem31,
+
+ // Portrait (not an index)
+ kPortraitOriginal = 32,
+ kPortraitGreen = 34,
+ kPortraitYellow = 36,
+
+ kItemInvalid = 128,
+
+ kItem146 = 146,
+ kItem147 = 147,
+
+ // Toggles
+ kItemToggleHigh = 0x7F,
+ kItemToggleLow = 0xF7
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Object ID
+//////////////////////////////////////////////////////////////////////////
+enum ObjectIndex {
+ kObjectNone,
+ kObjectCompartment1,
+ kObjectCompartment2,
+ kObjectCompartment3,
+ kObjectCompartment4,
+ kObjectCompartment5, // 5
+ kObjectCompartment6,
+ kObjectCompartment7,
+ kObjectCompartment8,
+ kObjectOutsideTylerCompartment,
+ kObject10, // 10
+ kObject11,
+ kObject12,
+ kObject13,
+ kObject14,
+ kObject15, // 15
+ kObject16,
+ kObjectHandleBathroom,
+ kObjectHandleInsideBathroom,
+ kObjectKitchen,
+ kObject20, // 20
+ kObject21,
+ kObject22,
+ kObjectTrainTimeTable,
+ kObjectRedSleepingCar,
+ kObject25, // 25
+ kObjectHandleOutsideLeft,
+ kObjectHandleOutsideRight,
+ kObject28,
+ kObject29,
+ kObject30, // 30
+ kObject31,
+ kObjectCompartmentA,
+ kObjectCompartmentB,
+ kObjectCompartmentC,
+ kObjectCompartmentD, // 35
+ kObjectCompartmentE,
+ kObjectCompartmentF,
+ kObjectCompartmentG,
+ kObjectCompartmentH,
+ kObject40, // 40
+ kObject41,
+ kObject42,
+ kObject43,
+ kObjectOutsideBetweenCompartments,
+ kObjectOutsideAnnaCompartment, // 45
+ kObject46,
+ kObject47,
+ kObject48, // might be the egg
+ kObject49,
+ kObject50, // 50
+ kObject51,
+ kObject52,
+ kObject53,
+ kObject54,
+ kObjectRestaurantCar, // 55
+ kObject56,
+ kObject57,
+ kObject58,
+ kObject59,
+ kObject60, // 60
+ kObject61,
+ kObject62,
+ kObject63,
+ kObject64,
+ kObject65, // 65
+ kObject66,
+ kObject67,
+ kObject68,
+ kObject69,
+ kObject70, // 70
+ kObject71,
+ kObject72,
+ kObjectCeiling,
+ kObject74,
+ kObjectCompartmentKronos, // 75
+ kObject76,
+ kObject77,
+ kObject78,
+ kObject79,
+ kObject80, // 80
+ kObject81,
+ kObject82,
+ kObject83,
+ kObject84,
+ kObject85, // 85
+ kObject86,
+ kObject87,
+ kObject88,
+ kObject89,
+ kObject90, // 90
+ kObject91,
+ kObject92,
+ kObject93,
+ kObject94,
+ kObject95, // 95
+ kObject96,
+ kObject97,
+ kObject98,
+ kObject99,
+ kObject100, // 100
+ kObject101,
+ kObject102,
+ kObject103,
+ kObject104,
+ kObject105, // 105
+ kObject106,
+ kObject107,
+ kObject108,
+ kObjectCageMax,
+ kObject110, // 110
+ kObject111,
+ kObject112,
+ kObject113,
+ kObject114,
+ kObject115, // 115
+ kObject116,
+ kObject117,
+ kObject118,
+ kObject119,
+ kObject120, // 120
+ kObject121,
+ kObject122,
+ kObject123,
+ kObject124,
+ kObject125, // 125
+ kObject126,
+ kObject127,
+ kObjectMax
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Entity ID
+//////////////////////////////////////////////////////////////////////////
+enum EntityIndex {
+ kEntityPlayer,
+ kEntityAnna,
+ kEntityAugust,
+ kEntityMertens,
+ kEntityCoudert,
+ kEntityPascale, // 5
+ kEntityServers0,
+ kEntityServers1,
+ kEntityCooks,
+ kEntityVerges,
+ kEntityTatiana, // 10
+ kEntityVassili,
+ kEntityAlexei,
+ kEntityAbbot,
+ kEntityMilos,
+ kEntityVesna, // 15
+ kEntityIvo,
+ kEntitySalko,
+ kEntityKronos,
+ kEntityKahina,
+ kEntityFrancois, // 20
+ kEntityMmeBoutarel,
+ kEntityBoutarel,
+ kEntityRebecca,
+ kEntitySophie,
+ kEntityMahmud, // 25
+ kEntityYasmin,
+ kEntityHadija,
+ kEntityAlouan,
+ kEntityGendarmes,
+ kEntityMax, // 30
+ kEntityChapters,
+ kEntityTrain,
+ kEntityTables0,
+ kEntityTables1,
+ kEntityTables2, // 35
+ kEntityTables3,
+ kEntityTables4,
+ kEntityTables5,
+ kEntity39,
+
+ kEntitySteam = 255
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Events
+// - a single D at the end means that Cath is on the right of the "scene" (D = Down the train, U = Up the train)
+// - DD: during the day, coming down the train
+// - DU: during the day, coming up the train
+// - ND: during the night, coming down the train
+// - NU: during the night, coming up the train
+//////////////////////////////////////////////////////////////////////////
+enum EventIndex {
+ kEventNone = 0,
+ kEventGotALight = 1,
+ kEventGotALightD = 2,
+ kEventDinerMindJoin = 3,
+ kEventDinerAugustOriginalJacket = 4,
+ kEventDinerAugust = 5,
+ kEventDinerAugustAlexeiBackground = 6,
+ kEventMeetAugustTylerCompartment = 7,
+ kEventMeetAugustTylerCompartmentBed = 8,
+ kEventMeetAugustHisCompartment = 9,
+ kEventMeetAugustHisCompartmentBed = 10,
+ kEventAugustFindCorpse = 11,
+ kEventAugustPresentAnna = 12,
+ kEventAugustPresentAnnaFirstIntroduction = 13,
+ kEventAnnaIntroductionRejected = 14,
+ kEventAnnaConversationGoodNight = 15,
+ kEventAnnaVisitToCompartmentGun = 16,
+ kEventInvalid_17 = 17,
+ kEventAnnaGoodNight = 18,
+ kEventAnnaGoodNightInverse = 19,
+ kEventAugustGoodMorning = 20,
+ kEventAugustMerchandise = 21,
+ kEventAugustTalkGold = 22,
+ kEventAugustTalkGoldDay = 23,
+ kEventAugustTalkCompartmentDoor = 24,
+ kEventAugustTalkCompartmentDoorBlueRedingote = 25,
+ kEventAugustLunch = 26,
+ kEventKronosVisit = 27,
+ kEventAnnaSearchingCompartment = 28,
+ kEventAugustBringEgg = 29,
+ kEventAugustBringBriefcase = 30,
+ kEventAugustTalkCigar = 31,
+ kEventAnnaBaggageArgument = 32,
+ kEventAnnaBagagePart2 = 33,
+ kEventAnnaConversation_34 = 34,
+ kEventAugustDrink = 35,
+ kEventAnnaTired = 36,
+ kEventAnnaTiredKiss = 37,
+ kEventAnnaBaggageTies = 38,
+ kEventAnnaBaggageTies2 = 39,
+ kEventAnnaBaggageTies3 = 40,
+ kEventAnnaBaggageTies4 = 41,
+ kEventAugustUnhookCarsBetrayal = 42,
+ kEventAugustUnhookCars = 43,
+ kEventLocomotiveAnnaStopsTrain = 44,
+ kEventInvalid_45 = 45,
+ kEventTrainStopped = 46,
+ kEventAnnaKissTrainHijacked = 47,
+ kEventTrainHijacked = 48,
+ kEventAnnaKilled = 49,
+ kEventKronosGoingToInvitation = 50,
+ kEventKronosConversation = 51,
+ kEventKahinaAskSpeakFirebird = 52,
+ kEventKahinaAskSpeak = 53,
+ kEventKronosConversationFirebird = 54,
+ kEventKahinaGunYellow = 55,
+ kEventKahinaGunBlue = 56,
+ kEventKahinaGun = 57,
+ kEventKronosBringEggCeiling = 58,
+ kEventKronosBringEgg = 59,
+ kEventKronosBringNothing = 60,
+ kEventKronosReturnBriefcase = 61,
+ kEventKronosHostageAnna = 62,
+ kEventKronosGiveFirebird = 63,
+ kEventKahinaPunchBaggageCarEntrance = 64,
+ kEventKahinaPunchBlue = 65,
+ kEventKahinaPunchYellow = 66,
+ kEventKahinaPunchSalon = 67,
+ kEventKahinaPunchKitchen = 68,
+ kEventKahinaPunchBaggageCar = 69,
+ kEventKahinaPunchCar = 70,
+ kEventKahinaPunchSuite4 = 71,
+ kEventKahinaPunchRestaurant = 72,
+ kEventKronosHostageAnnaNoFirebird = 73,
+ kEventKahinaPunch = 74,
+ kEventKahinaWrongDoor = 75,
+ kEventAlexeiDiner = 76,
+ kEventAlexeiDinerOriginalJacket = 77,
+ kEventAlexeiSalonVassili = 78,
+ kEventAlexeiSalonCath = 79,
+ kEventAlexeiSalonPoem = 80,
+ kEventTatianaAskMatchSpeakRussian = 81,
+ kEventTatianaAskMatch = 82,
+ kEventTatianaGivePoem = 83,
+ kEventVassiliSeizure = 84,
+ kEventTatianaBreakfastAlexei = 85,
+ kEventTatianaBreakfast = 86,
+ kEventTatianaBreakfastGivePoem = 87,
+ kEventTatianaAlexei = 88,
+ kEventTatianaCompartmentStealEgg = 89,
+ kEventTatianaCompartment = 90,
+ kEventVassiliCompartmentStealEgg = 91,
+ kEventTatianaTylerCompartment = 92,
+ kEventTylerCastleDream= 93,
+ kEventVassiliDeadAlexei = 94,
+ kEventCathFreePassengers = 95,
+ kEventTatianaVassiliTalk = 96,
+ kEventTatianaVassiliTalkNight = 97,
+ kEventMilosTylerCompartmentVisit = 98,
+ kEventMilosTylerCompartmentBedVisit = 99,
+ kEventMilosTylerCompartment = 100,
+ kEventMilosTylerCompartmentBed = 101,
+ kEventMilosTylerCompartmentDefeat = 102,
+ kEventMilosCorpseFloor = 103,
+ kEventMilosCompartmentVisitAugust = 104,
+ kEventMilosCorridorThanks = 105,
+ kEventMilosCorridorThanksD = 106,
+ kEventMilosCompartmentVisitTyler = 107,
+ kEventLocomotiveMilosDay = 108,
+ kEventLocomotiveMilosNight = 109,
+ kEventAbbotIntroduction = 110,
+ kEventAbbotWrongCompartment = 111,
+ kEventAbbotWrongCompartmentBed = 112,
+ kEventAbbotInvitationDrink = 113,
+ kEventAbbotDrinkGiveDetonator = 114,
+ kEventTrainExplosionBridge = 115,
+ kEventDefuseBomb = 116,
+ kEventAbbotDrinkDefuse = 117,
+ kEventMertensLastCar = 118,
+ kEventMertensLastCarOriginalJacket = 119,
+ kEventMertensKronosInvitation = 120,
+ kEventMertensKronosInvitationCompartment = 121,
+ kEventMertensKronosInvitationClosedWindows = 122,
+ kEventMertensBloodJacket = 123,
+ kEventCoudertBloodJacket = 124,
+ kEventMertensCorpseFloor = 125,
+ kEventMertensCorpseBed = 126,
+ kEventMertensDontMakeBed = 127,
+ kEventInvalid_128 = 128,
+ kEventGendarmesArrestation = 129,
+ kEventVergesSuitcase = 130,
+ kEventVergesSuitcaseStart = 131,
+ kEventVergesSuitcaseOtherEntry = 132,
+ kEventVergesSuitcaseOtherEntryStart = 133,
+ kEventVergesSuitcaseNight = 134,
+ kEventVergesSuitcaseNightStart = 135,
+ kEventVergesSuitcaseNightOtherEntry = 136,
+ kEventVergesSuitcaseNightOtherEntryStart = 137,
+ kEventMertensAskTylerCompartment = 138,
+ kEventMertensAskTylerCompartmentD = 139,
+ kEventMertensPushCall = 140,
+ kEventMertensPushCallNight = 141,
+ kEventMertensAugustWaiting = 142,
+ kEventMertensAugustWaitingCompartment = 143,
+ kEventIntroBroderbrund = 144,
+ kEventCoudertAskTylerCompartment = 145,
+ kEventMertensKronosConcertInvitation = 146,
+ kEventCoudertGoingOutOfVassiliCompartment = 147,
+ kEventLocomotiveConductorsDiscovered = 148,
+ kEventLocomotiveConductorsLook = 149,
+ kEventMahmudWrongDoor = 150,
+ kEventMahmudWrongDoorOriginalJacket = 151,
+ kEventMahmudWrongDoorDay = 152,
+ kEventVergesEscortToDiningCar = 153,
+ kEventVergesBaggageCarOffLimits = 154,
+ kEventVergesCanIHelpYou = 155,
+ kEventCoudertBaggageCar = 156,
+ kEventCathTurningDay = 157,
+ kEventCathTurningNight = 158,
+ kEventIntro = 159,
+ kEventCathDream = 160,
+ kEventCorpseDropBridge = 161,
+ kEventTrainPassing = 162,
+ kEventVergesAnnaDead = 163,
+ kEventViennaAugustUnloadGuns = 164,
+ kEventViennaKronosFirebird = 165,
+ kEventViennaContinueGame = 166,
+ kEventCathVesnaRestaurantKilled = 167,
+ kEventCathMaxCage = 168,
+ kEventCathMaxFree = 169,
+ kEventCathMaxLickHand = 170,
+ kEventCathIvoFight = 171,
+ kEventCathSalkoTrainTopFight = 172,
+ kEventCathVesnaTrainTopFight = 173,
+ kEventCathVesnaTrainTopKilled = 174,
+ kEventCathVesnaTrainTopWin = 175,
+ kEventCathSalkoTrainTopWin = 176,
+ kEventFrancoisWhistle = 177,
+ kEventFrancoisWhistleD = 178,
+ kEventFrancoisWhistleNight = 179,
+ kEventFrancoisWhistleNightD = 180,
+ kEventFrancoisShowBeetle = 181,
+ kEventFrancoisShowBeetleD = 182,
+ kEventFrancoisTradeWhistle = 183,
+ kEventFrancoisTradeWhistleD = 184,
+ kEventFrancoisShowEgg = 185,
+ kEventFrancoisShowEggD = 186,
+ kEventFrancoisShowEggNight = 187,
+ kEventFrancoisShowEggNightD = 188,
+ kEventKronosBringFirebird = 189,
+ kEventKronosOpenFirebird = 190,
+ kEventFinalSequence = 191,
+ kEventLocomotiveRestartTrain = 192,
+ kEventLocomotiveOldBridge = 193,
+ kEventLocomotiveAbbotGetSomeRest = 194,
+ kEventLocomotiveAbbotShoveling = 195,
+ kEventLocomotiveMilosShovelingDay = 196,
+ kEventLocomotiveMilosShovelingNight = 197,
+ kEventAnnaGiveScarf = 198,
+ kEventAnnaGiveScarfDiner = 199,
+ kEventAnnaGiveScarfSalon = 200,
+ kEventAnnaGiveScarfMonogram = 201,
+ kEventAnnaGiveScarfDinerMonogram = 202,
+ kEventAnnaGiveScarfSalonMonogram = 203,
+ kEventAnnaGiveScarfAsk = 204,
+ kEventAnnaGiveScarfDinerAsk = 205,
+ kEventAnnaGiveScarfSalonAsk = 206,
+ kEventAugustArrivalInMunich = 207,
+ kEventAnnaDialogGoToJerusalem = 208,
+ kEventConcertStart = 209,
+ kEventConcertEnd = 210,
+ kEventCathFallingAsleep = 211,
+ kEventCathWakingUp = 212,
+ kEventConcertCough = 213,
+ kEventConcertSit = 214,
+ kEventConcertLeaveWithBriefcase = 215,
+ kEventCorpseDropFloorOriginal = 216,
+ kEventCorpseDropFloorGreen = 217,
+ kEventCorpsePickFloorOriginal = 218,
+ kEventCorpsePickFloorGreen = 219,
+ kEventCorpsePickFloorOpenedBedOriginal = 220,
+ kEventCorpsePickBedOriginal = 221,
+ kEventCorpsePickBedGreen = 222,
+ kEventCorpseDropBedOriginal = 223,
+ kEventCorpseDropBedGreen = 224,
+ kEventCorpseDropWindowOriginal = 225,
+ kEventCorpseDropWindowGreen = 226,
+ kEventCathFindCorpse = 227,
+ kEventCathLookOutsideWindowDay = 228,
+ kEventCathLookOutsideWindowNight = 229,
+ kEventCathGoOutsideTylerCompartmentDay = 230,
+ kEventCathGoOutsideTylerCompartmentNight = 231,
+ kEventCathGoOutsideDay = 232,
+ kEventCathGoOutsideNight = 233,
+ kEventCathSlipTylerCompartmentDay = 234,
+ kEventCathSlipTylerCompartmentNight = 235,
+ kEventCathSlipDay = 236,
+ kEventCathSlipNight = 237,
+ kEventCathGetInsideTylerCompartmentDay = 238,
+ kEventCathGetInsideTylerCompartmentNight = 239,
+ kEventCathGetInsideDay = 240,
+ kEventCathGetInsideNight = 241,
+ kEventCathGettingInsideAnnaCompartment = 242,
+ kEventCathClimbUpTrainGreenJacket = 243,
+ kEventCathClimbUpTrainNoJacketNight = 244,
+ kEventCathClimbUpTrainNoJacketDay = 245,
+ kEventCathClimbDownTrainGreenJacket = 246,
+ kEventCathClimbDownTrainNoJacketNight = 247,
+ kEventCathClimbDownTrainNoJacketDay= 248,
+ kEventCathTopTrainGreenJacket = 249,
+ kEventCathTopTrainNoJacketNight = 250,
+ kEventCathTopTrainNoJacketDay = 251,
+ kEventCathBreakCeiling = 252,
+ kEventCathJumpDownCeiling = 253,
+ kEventCathJumpUpCeilingBriefcase = 254,
+ kEventCathJumpUpCeiling = 255,
+ kEventPickGreenJacket = 256,
+ kEventPickScarfGreen = 257,
+ kEventPickScarfOriginal = 258,
+ kEventCloseMatchbox = 259,
+ kEventCathStruggleWithBonds = 260,
+ kEventCathBurnRope = 261,
+ kEventCathRemoveBonds = 262,
+ kEventCathStruggleWithBonds2 = 263,
+ kEventCathDefusingBomb = 264,
+ kEventCathSmokeNight = 265,
+ kEventCathSmokeDay = 266,
+ kEventCathOpenEgg = 267,
+ kEventCathOpenEggNoBackground = 268,
+ kEventCathCloseEgg = 269,
+ kEventCathCloseEggNoBackground = 270,
+ kEventCathUseWhistleOpenEgg = 271,
+ kEventCathUseWhistleOpenEggNoBackground = 272
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Action ID (used by entity logic)
+//////////////////////////////////////////////////////////////////////////
+enum ActionIndex {
+ kActionNone = 0,
+ kAction1 = 1,
+ kActionEndSound = 2,
+ kActionExitCompartment = 3,
+ kAction4 = 4,
+ kActionExcuseMeCath = 5,
+ kActionExcuseMe = 6,
+ kActionKnock = 8,
+ kActionOpenDoor = 9,
+ kAction10 = 10,
+ kAction11 = 11,
+ kActionDefault = 12,
+ kAction16 = 16,
+ kActionDrawScene = 17,
+ kActionCallback = 18,
+
+ /////////////////////////////
+ // Abbot
+ /////////////////////////////
+ kAction100969180 = 100969180, // Anna
+ kAction101169422 = 101169422,
+ kAction104060776 = 104060776,
+ kAction135600432 = 135600432,
+ kAction136196244 = 136196244,
+ kAction157159392 = 157159392,
+ kAction157489665 = 157489665,
+ kAction158480160 = 158480160,
+ kAction192054567 = 192054567,
+ kAction203073664 = 203073664,
+ kAction222609266 = 222609266,
+
+ /////////////////////////////
+ // Alexei
+ /////////////////////////////
+ kAction100906246 = 100906246,
+ kAction123536024 = 123536024,
+ kAction124697504 = 124697504,
+ kAction135664192 = 135664192,
+ kAction135854208 = 135854208,
+ kAction188784532 = 188784532,
+ kAction221617184 = 221617184,
+
+ /////////////////////////////
+ // Alouan
+ /////////////////////////////
+ kAction189489753 = 189489753,
+ kAction190219584 = 190219584, // Francois
+
+ /////////////////////////////
+ // Anna
+ /////////////////////////////
+ kAction136702400 = 136702400,
+ kAction139254416 = 139254416,
+ kAction156049968 = 156049968,
+ kAction157370960 = 157370960,
+ kAction157894320 = 157894320,
+ kAction159332865 = 159332865, // August
+ kAction189299008 = 189299008,
+ kAction191668032 = 191668032, // some action during or before concert?
+ kAction201437056 = 201437056,
+ kAction235856512 = 235856512,
+ kAction236060709 = 236060709,
+ kAction238936000 = 238936000,
+ kAction259136835 = 259136835,
+ kAction291662081 = 291662081,
+
+
+ /////////////////////////////
+ // August
+ /////////////////////////////
+ kAction123793792 = 123793792,
+ kAction134611040 = 134611040,
+ kAction168046720 = 168046720,
+ kAction168627977 = 168627977,
+ kAction169032608 = 169032608,
+ kAction189426612 = 189426612,
+ kAction203859488 = 203859488,
+ kAction219522616 = 219522616, // Servers0
+ kAction225182640 = 225182640,
+ kAction235257824 = 235257824,
+
+ /////////////////////////////
+ // Boutarel
+ /////////////////////////////
+ kAction125039808 = 125039808,
+ kAction134466544 = 134466544,
+ kAction135854206 = 135854206,
+ kAction159003408 = 159003408,
+ kAction203520448 = 203520448,
+ kAction237889408 = 237889408,
+
+ /////////////////////////////
+ // Chapters
+ /////////////////////////////
+ kAction135800432 = 135800432,
+ kActionChapter3 = 139122728,
+ kActionChapter5 = 139254416,
+ kAction156435676 = 156435676,
+ kAction169629818 = 169629818,
+ kAction171843264 = 171843264,
+ kAction190346110 = 190346110,
+
+ /////////////////////////////
+ // Cooks
+ /////////////////////////////
+ kAction101632192 = 101632192,
+ kAction224849280 = 224849280,
+ kAction236976550 = 236976550,
+
+ /////////////////////////////
+ // Coudert
+ /////////////////////////////
+ kAction123733488 = 123733488,
+ kAction154005632 = 154005632,
+ kAction155991520 = 155991520,
+ kAction157026693 = 157026693,
+ kAction168253822 = 168253822,
+ kAction168254872 = 168254872,
+ kAction168316032 = 168316032, // Tatiana
+ kAction169557824 = 169557824,
+ kAction171394341 = 171394341, // Mertens
+ kAction185671840 = 185671840,
+ kAction185737168 = 185737168,
+ kAction188570113 = 188570113,
+ kAction189026624 = 189026624,
+ kAction189750912 = 189750912,
+ kAction192063264 = 192063264, // Anna
+ kAction201431954 = 201431954, // Mertens / Verges
+ kAction201439712 = 201439712,
+ kAction205033696 = 205033696,
+ kAction205346192 = 205346192, // Francois
+ kAction219971920 = 219971920, // Anna
+ kAction223068211 = 223068211, // MmeBoutarel
+ kAction225932896 = 225932896,
+ kAction226031488 = 226031488, // Verges
+ kAction235061888 = 235061888, // Tatiana
+ kAction238358920 = 238358920, // Anna
+ kAction253868128 = 253868128, // Anna
+ kAction285528346 = 285528346, // Rebecca
+ kAction292048641 = 292048641,
+ kAction305159806 = 305159806,
+ kAction326348944 = 326348944,
+ kAction339669520 = 339669520, // Verges
+
+ /////////////////////////////
+ // Francois
+ /////////////////////////////
+ kAction100901266 = 100901266,
+ kAction100957716 = 100957716,
+ kAction101107728 = 101107728,
+ kAction189872836 = 189872836,
+ kAction190390860 = 190390860,
+
+ /////////////////////////////
+ // Gendarmes
+ /////////////////////////////
+ kAction168710784 = 168710784,
+ kAction169499649 = 169499649,
+
+ /////////////////////////////
+ // Kahina
+ /////////////////////////////
+ kAction92186062 = 92186062,
+ kAction137503360 = 137503360,
+ kAction237555748 = 237555748,
+
+ /////////////////////////////
+ // Kronos
+ /////////////////////////////
+ kAction137685712 = 137685712,
+ kAction138085344 = 138085344,
+ kAction171849314 = 171849314,
+ kAction235599361 = 235599361,
+
+ /////////////////////////////
+ // Mahmud
+ /////////////////////////////
+ kAction102227384 = 102227384, // Mertens
+ kAction156567128 = 156567128,
+ kAction170483072 = 170483072,
+ kAction225563840 = 225563840,
+
+ /////////////////////////////
+ // Max
+ /////////////////////////////
+ kAction71277948 = 71277948,
+ kAction158007856 = 158007856,
+ kAction101687594 = 101687594,
+ kAction122358304 = 122358304, // also Servers1/Boutarel?
+ kActionMaxFreeFromCage = 135204609,
+ kAction156622016 = 156622016,
+
+ /////////////////////////////
+ // Mertens
+ /////////////////////////////
+ kAction155604840 = 155604840, // MmeBoutarel
+ kAction169633856 = 169633856,
+ kAction188635520 = 188635520,
+ kAction190082817 = 190082817,
+ kAction192849856 = 192849856,
+ kAction204379649 = 204379649,
+ kAction224122407 = 224122407,
+ kAction238732837 = 238732837,
+ kAction238790488 = 238790488, // Tatiana
+ kAction269436673 = 269436673,
+ kAction269624833 = 269624833,
+ kAction302614416 = 302614416,
+ kAction303343617 = 303343617,
+
+ /////////////////////////////
+ // Milos
+ /////////////////////////////
+ kAction88652208 = 88652208, // Coudert
+ kAction122865568 = 122865568,
+ kAction123852928 = 123852928,
+ kAction123199584 = 123199584, // Coudert
+ kAction157691176 = 157691176,
+ kAction208228224 = 208228224,
+ kAction221683008 = 221683008,
+ kAction259125998 = 259125998,
+
+ /////////////////////////////
+ // Mme Boutarel
+ /////////////////////////////
+ kAction102484312 = 102484312,
+ kAction102752636 = 102752636,
+ kAction134289824 = 134289824,
+ kAction168986720 = 168986720,
+ kAction202221040 = 202221040,
+ kAction242526416 = 242526416,
+
+ /////////////////////////////
+ // Pascale
+ /////////////////////////////
+ kAction101824388 = 101824388,
+ kAction136059947 = 136059947,
+ kAction169750080 = 169750080,
+ kAction190605184 = 190605184,
+ kAction191604416 = 191604416,
+ kAction207769280 = 207769280,
+ kAction223262556 = 223262556,
+ kAction239072064 = 239072064,
+ kAction257489762 = 257489762,
+ kAction269479296 = 269479296,
+ kAction352703104 = 352703104,
+ kAction352768896 = 352768896,
+
+ /////////////////////////////
+ // Rebecca
+ /////////////////////////////
+ kAction125496184 = 125496184,
+ kAction155465152 = 155465152,
+ kAction155980128 = 155980128,
+ kAction169358379 = 169358379,
+ kAction224253538 = 224253538,
+ kAction254915200 = 254915200,
+
+ /////////////////////////////
+ // Salko
+ /////////////////////////////
+ kAction55996766 = 55996766,
+ kAction101169464 = 101169464,
+ kAction102675536 = 102675536, // Ivo
+ kAction136184016 = 136184016,
+
+ /////////////////////////////
+ // Servers 0
+ /////////////////////////////
+ kAction170016384 = 170016384,
+ kAction188893625 = 188893625,
+ kAction201964801 = 201964801, // August
+ kAction204704037 = 204704037,
+ kAction207330561 = 207330561,
+ kAction218128129 = 218128129,
+ kAction218586752 = 218586752,
+ kAction218983616 = 218983616,
+ kAction223712416 = 223712416,
+ kAction237485916 = 237485916,
+ kAction252568704 = 252568704,
+ kAction268773672 = 268773672, // Anna / August
+ kAction270068760 = 270068760,
+ kAction270410280 = 270410280,
+ kAction286403504 = 286403504,
+ kAction286534136 = 286534136,
+ kAction292758554 = 292758554,
+ kAction304061224 = 304061224,
+ kAction337548856 = 337548856,
+
+ /////////////////////////////
+ // Servers 1
+ /////////////////////////////
+ kAction101106391 = 101106391,
+ kAction122288808 = 122288808, // Boutarel
+ kAction123712592 = 123712592, // Ivo
+ kAction125826561 = 125826561, // August
+ kAction134486752 = 134486752, // August
+ kAction168717392 = 168717392, // Boutarel
+ kAction189688608 = 189688608,
+ kAction219377792 = 219377792,
+ kAction223002560 = 223002560,
+ kAction236237423 = 236237423,
+ kAction256200848 = 256200848,
+ kAction258136010 = 258136010,
+ kAction269485588 = 269485588,
+ kAction291721418 = 291721418,
+ kAction302203328 = 302203328,
+ kAction302996448 = 302996448,
+ kAction326144276 = 326144276,
+
+ /////////////////////////////
+ // Sophie
+ /////////////////////////////
+ kActionProceedChapter5 = 70549068,
+ kAction123668192 = 123668192,
+ kAction125242096 = 125242096,
+ kAction136654208 = 136654208,
+ kAction259921280 = 259921280,
+ kAction292775040 = 292775040,
+
+ /////////////////////////////
+ // Tables
+ /////////////////////////////
+ kActionDrawTablesWithChairs = 103798704,
+ kAction136455232 = 136455232,
+
+ /////////////////////////////
+ // Tatiana
+ /////////////////////////////
+ kAction69239528 = 69239528,
+ kAction123857088 = 123857088,
+ kAction124973510 = 124973510,
+ kAction154071333 = 154071333,
+ kAction156444784 = 156444784,
+ kAction169360385 = 169360385,
+ kAction191198209 = 191198209,
+ kAction223183000 = 223183000, // August
+ kAction236053296 = 236053296, // Alexei
+ kAction236241630 = 236241630, // Anna
+ kAction236517970 = 236517970, // Anna
+ kAction268620864 = 268620864, // August
+ kAction290869168 = 290869168,
+
+ /////////////////////////////
+ // Train
+ /////////////////////////////
+ kAction191070912 = 191070912,
+ kActionTrainStopRunning = 191350523,
+ kActionCatchBeetle = 202613084,
+ kAction203339360 = 203339360,
+ kActionTrainStartRunning = 203419131,
+ kAction203863200 = 203863200,
+ kAction222746496 = 222746496,
+ kActionBreakCeiling = 225056224,
+ kAction290410610 = 290410610,
+ kActionJumpDownCeiling = 338494260,
+
+ /////////////////////////////
+ // Verges
+ /////////////////////////////
+ kAction125233040 = 125233040, // Abbot
+ kAction125499160 = 125499160,
+ kAction155853632 = 155853632,
+ kAction158617345 = 158617345,
+ kAction167854368 = 167854368,
+ kAction168187490 = 168187490,
+ kAction168255788 = 168255788,
+ kActionDeliverMessageToTyler = 191337656,
+ kAction202558662 = 202558662,
+
+ /////////////////////////////
+ // Vassili
+ /////////////////////////////
+ kAction122732000 = 122732000,
+ kAction168459827 = 168459827,
+ kAction191477936 = 191477936,
+
+ /////////////////////////////
+ // Vesna
+ /////////////////////////////
+ kAction124190740 = 124190740,
+ kAction134427424 = 134427424,
+ kAction135024800 = 135024800,
+ kAction137165825 = 137165825,
+ kAction155913424 = 155913424,
+ kAction190412928 = 190412928,
+ kAction203663744 = 203663744,
+ kAction204832737 = 204832737,
+
+ /////////////////////////////
+ // Misc
+ /////////////////////////////
+ kAction158610240 = 158610240,
+ kAction167992577 = 167992577,
+ kAction168646401 = 168646401,
+ kAction169300225 = 169300225,
+ kAction169773228 = 169773228,
+ kActionEndChapter = 190346110,
+ kAction191001984 = 191001984,
+ kAction192637492 = 192637492,
+ kAction201959744 = 201959744,
+ kAction202621266 = 202621266,
+ kAction202884544 = 202884544,
+ kAction203078272 = 203078272,
+ kAction205034665 = 205034665,
+ kAction205294778 = 205294778,
+ kActionUseWhistle = 270751616,
+ kAction272177921 = 272177921,
+ kAction224309120 = 224309120,
+ kAction225358684 = 225358684,
+ kAction225367984 = 225367984,
+ kAction226078300 = 226078300, // Whistle
+
+ kActionEnd
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Functors classes used by the engine
+//////////////////////////////////////////////////////////////////////////
+
+// FIXME is this achievable with the existing Functor1Mem function
+template<class Arg, class Res, class T>
+class Functor1MemConst : public Common::Functor1<Arg, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg) const;
+
+ Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg v1) const {
+ return (_t->*_func)(v1);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+// FIXME move this to existing func.h file
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Result>
+struct QuaternaryFunction {
+ typedef Arg1 FirstArgumentType;
+ typedef Arg2 SecondArgumentType;
+ typedef Arg3 ThirdArgumentType;
+ typedef Arg4 FourthArgumentType;
+ typedef Result ResultType;
+};
+
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Res>
+struct Functor4 : public QuaternaryFunction<Arg1, Arg2, Arg3, Arg4, Res> {
+ virtual ~Functor4() {}
+
+ virtual bool isValid() const = 0;
+ virtual Res operator()(Arg1, Arg2, Arg3, Arg4) const = 0;
+};
+
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Res, class T>
+class Functor4Mem : public Functor4<Arg1, Arg2, Arg3, Arg4, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg1, Arg2, Arg3, Arg4);
+
+ Functor4Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg1 v1, Arg2 v2, Arg3 v3, Arg4 v4) const {
+ return (_t->*_func)(v1, v2, v3, v4);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SHARED_H
diff --git a/engines/lure/debugger.cpp b/engines/lure/debugger.cpp
index 1cfe0804e4..9b1bb743e4 100644
--- a/engines/lure/debugger.cpp
+++ b/engines/lure/debugger.cpp
@@ -23,7 +23,6 @@
*
*/
-
#include "common/config-manager.h"
#include "common/endian.h"
#include "lure/luredefs.h"
@@ -323,7 +322,7 @@ bool Debugger::cmd_hotspot(int argc, const char **argv) {
if (h != NULL) {
DebugPrintf("Frame Number = %d of %d\n", h->frameNumber(), h->numFrames());
- DebugPrintf("Persistant = %s\n", h->persistant() ? "true" : "false");
+ DebugPrintf("Persistent = %s\n", h->persistant() ? "true" : "false");
}
} else if (strcmp(argv[2], "actions") == 0) {
@@ -353,15 +352,13 @@ bool Debugger::cmd_hotspot(int argc, const char **argv) {
} else {
if (strcmp(argv[2], "schedule") == 0) {
// List any current schedule for the character
- hs->npcSchedule.list(buffer);
- DebugPrintf("%s", buffer);
+ DebugPrintf("%s", hs->npcSchedule.getDebugInfo().c_str());
}
if (!h)
DebugPrintf("The specified hotspot is not currently active\n");
else if (strcmp(argv[2], "paths") == 0) {
// List any paths for a charcter
- h->pathFinder().list(buffer);
- DebugPrintf("%s", buffer);
+ DebugPrintf("%s", h->pathFinder().getDebugInfo().c_str());
}
else if (strcmp(argv[2], "pixels") == 0) {
// List the pixel data for the hotspot
diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp
index 30b6f840fc..bce98b28fd 100644
--- a/engines/lure/hotspots.cpp
+++ b/engines/lure/hotspots.cpp
@@ -2496,10 +2496,9 @@ void HotspotTickHandlers::standardCharacterAnimHandler(Hotspot &h) {
bool bumpedPlayer;
if (h.currentActions().action() != WALKING) {
- char buffer[MAX_DESC_SIZE];
- h.currentActions().list(buffer);
+ Common::String buffer = h.currentActions().getDebugInfo();
debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot standard character p=(%d,%d,%d) bs=%d\n%s",
- h.x(), h.y(), h.roomNumber(), h.blockedState(), buffer);
+ h.x(), h.y(), h.roomNumber(), h.blockedState(), buffer.c_str());
}
// Handle any active talk dialog
@@ -2902,12 +2901,12 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
Action hsAction;
uint16 hotspotId;
HotspotData *hotspot;
- char buffer[MAX_DESC_SIZE];
+ Common::String buffer;
- h.currentActions().list(buffer);
+ buffer = h.currentActions().getDebugInfo();
debugC(ERROR_DETAILED, kLureDebugAnimations,
"Hotspot player anim handler p=(%d,%d,%d) bs=%d\n%s",
- h.x(), h.y(), h.roomNumber(), h.blockedState(), buffer);
+ h.x(), h.y(), h.roomNumber(), h.blockedState(), buffer.c_str());
h.handleTalkDialog();
@@ -3037,10 +3036,10 @@ void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
if (pfResult == PF_UNFINISHED) break;
// Pathfinding is now complete
- pathFinder.list(buffer);
+ buffer = pathFinder.getDebugInfo();
debugC(ERROR_DETAILED, kLureDebugAnimations,
"Pathfind processing done; result=%d, walkFlag=%d\n%s",
- pfResult, h.walkFlag(), buffer);
+ pfResult, h.walkFlag(), buffer.c_str());
if ((pfResult != PF_OK) && (h.walkFlag() || (pfResult != PF_DEST_OCCUPIED))) {
@@ -4170,6 +4169,7 @@ PathFinderResult PathFinder::process() {
_inProgress = true;
initVars();
+ Common::Point diff(_destX - _xCurrent, _destY - _yCurrent);
_xCurrent >>= 3; _yCurrent >>= 3;
_xDestCurrent >>= 3; _yDestCurrent >>= 3;
if ((_xCurrent == _xDestCurrent) && (_yCurrent == _yDestCurrent)) {
@@ -4178,6 +4178,10 @@ PathFinderResult PathFinder::process() {
add(RIGHT, _xDestPos);
else if (_xDestPos < 0)
add(LEFT, -_xDestPos);
+ else if (diff.y > 0)
+ add(DOWN, diff.y);
+ else
+ add(UP, -diff.y);
_inProgress = false;
result = PF_OK;
@@ -4353,7 +4357,7 @@ PathFinderResult PathFinder::process() {
break;
}
- // Add a final move if necessary
+ // Add final movement if necessary
if (result == PF_OK) {
if (_xDestPos < 0)
@@ -4369,25 +4373,17 @@ final_step:
return result;
}
-void PathFinder::list(char *buffer) {
- if (buffer) {
- sprintf(buffer, "Pathfinder::list\n");
- buffer += strlen(buffer);
- }
- else {
- printf("Pathfinder::list\n");
- }
+Common::String PathFinder::getDebugInfo() const {
+ Common::String buffer;
+ buffer += "Pathfinder::list(\n";
- WalkingActionList::iterator i;
+ WalkingActionList::const_iterator i;
for (i = _list.begin(); i != _list.end(); ++i) {
WalkingActionEntry *e = (*i).get();
- if (buffer) {
- sprintf(buffer, "Direction=%d, numSteps=%d\n", e->direction(), e->numSteps());
- buffer += strlen(buffer);
- }
- else
- printf("Direction=%d, numSteps=%d\n", e->direction(), e->numSteps());
+ buffer += Common::String::format("Direction=%d, numSteps=%d\n", e->direction(), e->numSteps());
}
+
+ return buffer;
}
void PathFinder::processCell(uint16 *p) {
diff --git a/engines/lure/hotspots.h b/engines/lure/hotspots.h
index 5ff0ec563f..2ae2e91ecf 100644
--- a/engines/lure/hotspots.h
+++ b/engines/lure/hotspots.h
@@ -158,8 +158,7 @@ public:
void clear();
void reset(RoomPathsData &src);
PathFinderResult process();
- void list(char *buffer);
- void list() { list(NULL); }
+ Common::String getDebugInfo() const;
void pop() { _list.erase(_list.begin()); }
WalkingActionEntry &top() { return **_list.begin(); }
diff --git a/engines/lure/lure.h b/engines/lure/lure.h
index 15336a3507..297fb20f59 100644
--- a/engines/lure/lure.h
+++ b/engines/lure/lure.h
@@ -46,10 +46,10 @@
/**
* This is the namespace of the Lure engine.
*
- * Status of this engine: ???
+ * Status of this engine: Complete
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Lure of the Temptress
*/
namespace Lure {
diff --git a/engines/lure/menu.cpp b/engines/lure/menu.cpp
index 58364d4cb1..76b1d78823 100644
--- a/engines/lure/menu.cpp
+++ b/engines/lure/menu.cpp
@@ -629,6 +629,8 @@ uint16 PopupMenu::Show(int numEntries, const char *actions[]) {
}
bail_out:
+ delete s;
+
#ifndef LURE_CLICKABLE_MENUS
mouse.setPosition(oldX, oldY);
mouse.cursorOn();
diff --git a/engines/lure/res.cpp b/engines/lure/res.cpp
index 4342a1d6ad..90c2b67b85 100644
--- a/engines/lure/res.cpp
+++ b/engines/lure/res.cpp
@@ -33,8 +33,6 @@
namespace Lure {
-using namespace Common;
-
static Resources *int_resources = NULL;
Resources &Resources::getReference() {
diff --git a/engines/lure/res_struct.cpp b/engines/lure/res_struct.cpp
index a75ba6eac8..f6a2092ddc 100644
--- a/engines/lure/res_struct.cpp
+++ b/engines/lure/res_struct.cpp
@@ -875,7 +875,7 @@ CharacterScheduleEntry::CharacterScheduleEntry(CharacterScheduleEntry *src) {
_parent = src->_parent;
_action = src->_action;
_numParams = src->_numParams;
- Common::copy(src->_params, src->_params + MAX_TELL_COMMANDS * 3 * sizeof(uint16), _params);
+ Common::copy(src->_params, src->_params + MAX_TELL_COMMANDS * 3, _params);
}
uint16 CharacterScheduleEntry::param(int index) {
@@ -1439,77 +1439,42 @@ CurrentActionEntry *CurrentActionEntry::loadFromStream(Common::ReadStream *strea
return result;
}
-void CurrentActionStack::list(char *buffer) {
- ActionsList::iterator i;
+Common::String CurrentActionStack::getDebugInfo() const {
+ Common::String buffer;
+ ActionsList::const_iterator i;
- if (buffer) {
- sprintf(buffer, "CurrentActionStack::list num_actions=%d\n", size());
- buffer += strlen(buffer);
- }
- else
- printf("CurrentActionStack::list num_actions=%d\n", size());
+ buffer += Common::String::format("CurrentActionStack::list num_actions=%d\n", size());
for (i = _actions.begin(); i != _actions.end(); ++i) {
CurrentActionEntry *entry = (*i).get();
- if (buffer) {
- sprintf(buffer, "style=%d room#=%d", entry->action(), entry->roomNumber());
- buffer += strlen(buffer);
- }
- else
- printf("style=%d room#=%d", entry->action(), entry->roomNumber());
+ buffer += Common::String::format("style=%d room#=%d", entry->action(), entry->roomNumber());
if (entry->hasSupportData()) {
CharacterScheduleEntry &rec = entry->supportData();
- if (buffer) {
- sprintf(buffer, ", action=%d params=", rec.action());
- buffer += strlen(buffer);
- }
- else
- printf(", action=%d params=", rec.action());
+ buffer += Common::String::format(", action=%d params=", rec.action());
if (rec.numParams() == 0)
- if (buffer) {
- strcat(buffer, "none");
- buffer += strlen(buffer);
- }
- else
- printf("none");
+ buffer += "none";
else {
- for (int ctr = 0; ctr < rec.numParams(); ++ctr) {
- if (ctr != 0) {
- if (buffer) {
- strcpy(buffer, ", ");
- buffer += strlen(buffer);
- }
- else
- printf(", ");
- }
-
- if (buffer) {
- sprintf(buffer, "%d", rec.param(ctr));
- buffer += strlen(buffer);
- } else
- printf("%d", rec.param(ctr));
+ buffer += Common::String::format("%d", rec.param(0));
+ for (int ctr = 1; ctr < rec.numParams(); ++ctr) {
+ buffer += Common::String::format(", %d", rec.param(ctr));
}
}
}
- if (buffer) {
- sprintf(buffer, "\n");
- buffer += strlen(buffer);
- }
- else
- printf("\n");
+ buffer += "\n";
}
+
+ return buffer;
}
void CurrentActionStack::saveToStream(Common::WriteStream *stream) {
ActionsList::iterator i;
debugC(ERROR_DETAILED, kLureDebugAnimations, "Saving hotspot action stack");
- char buffer[MAX_DESC_SIZE];
- list(buffer);
- debugC(ERROR_DETAILED, kLureDebugAnimations, "%s", buffer);
+ Common::String buffer = getDebugInfo();
+ debugC(ERROR_DETAILED, kLureDebugAnimations, "%s", buffer.c_str());
for (i = _actions.begin(); i != _actions.end(); ++i) {
CurrentActionEntry *rec = (*i).get();
diff --git a/engines/lure/res_struct.h b/engines/lure/res_struct.h
index afb8ee9153..e4b8c02f6a 100644
--- a/engines/lure/res_struct.h
+++ b/engines/lure/res_struct.h
@@ -445,6 +445,10 @@ public:
void setRoomNumber(uint16 roomNum) { _roomNumber = roomNum; }
void setSupportData(CharacterScheduleEntry *newRec) {
assert((newRec == NULL) || (newRec->parent() != NULL));
+ if (_dynamicSupportData) {
+ delete _supportData;
+ _dynamicSupportData = false;
+ }
_supportData = newRec;
}
void setSupportData(uint16 entryId);
@@ -464,7 +468,7 @@ private:
public:
CurrentActionStack() { _actions.clear(); }
- bool isEmpty() { return _actions.begin() == _actions.end(); }
+ bool isEmpty() const { return _actions.begin() == _actions.end(); }
void clear() { _actions.clear(); }
CurrentActionEntry &top() { return **_actions.begin(); }
CurrentActionEntry &bottom() {
@@ -474,9 +478,8 @@ public:
}
CurrentAction action() { return isEmpty() ? NO_ACTION : top().action(); }
void pop() { _actions.erase(_actions.begin()); }
- int size() { return _actions.size(); }
- void list(char *buffer);
- void list() { list(NULL); }
+ int size() const { return _actions.size(); }
+ Common::String getDebugInfo() const;
void addBack(CurrentAction newAction, uint16 roomNum) {
_actions.push_back(ActionsList::value_type(new CurrentActionEntry(newAction, roomNum)));
diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp
index ca15ad8287..85beba8447 100644
--- a/engines/lure/sound.cpp
+++ b/engines/lure/sound.cpp
@@ -34,7 +34,7 @@
#include "common/endian.h"
#include "sound/midiparser.h"
-DECLARE_SINGLETON(Lure::SoundManager)
+DECLARE_SINGLETON(Lure::SoundManager);
namespace Lure {
diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp
index 23122eb960..c4113e00d0 100644
--- a/engines/m4/assets.cpp
+++ b/engines/m4/assets.cpp
@@ -28,6 +28,8 @@
#include "m4/compression.h"
#include "m4/graphics.h"
+#include "common/memstream.h"
+
namespace M4 {
BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) {
@@ -147,9 +149,9 @@ void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream
uint32 header = _stream->readUint32LE();
if (header == HEAD_M4SS) {
- printf("LE-encoded sprite\n");
+ debugC(kDebugGraphics, "LE-encoded sprite\n");
} else {
- printf("BE-encoded sprite\n");
+ debugC(kDebugGraphics, "BE-encoded sprite\n");
isBigEndian = true;
}
@@ -163,7 +165,7 @@ void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream
_stream->skip(6 * 4);
_frameCount = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
- printf("SpriteAsset::SpriteAsset() srcSize = %d; frameRate = %04X; pixelSpeed = %04X; maxWidth = %d; maxHeight = %d; frameCount = %d\n", _srcSize, _frameRate, _pixelSpeed, _maxWidth, _maxHeight, _frameCount);
+ debugC(kDebugGraphics, "SpriteAsset::SpriteAsset() srcSize = %d; frameRate = %04X; pixelSpeed = %04X; maxWidth = %d; maxHeight = %d; frameCount = %d\n", _srcSize, _frameRate, _pixelSpeed, _maxWidth, _maxHeight, _frameCount);
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
frameOffset = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
@@ -345,7 +347,7 @@ int32 SpriteAsset::parseSprite(bool isBigEndian) {
if (chunkType == CELS___SS) {
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
} else {
- warning("SpriteAsset::parseSprite() Expected chunk type %08X, got %08X", CELS___SS, chunkType);
+ debugC(kDebugGraphics, "SpriteAsset::parseSprite() Expected chunk type %08X, got %08X", CELS___SS, chunkType);
}
return chunkSize;
@@ -362,14 +364,14 @@ void SpriteAsset::loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndia
frameHeader.comp = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.frame = NULL;
_stream->seek(8 * 4, SEEK_CUR);
- //printf("SpriteAsset::loadFrameHeader() stream = %d; x = %d; y = %d; w = %d; h = %d; comp = %d\n", frameHeader.stream, frameHeader.x, frameHeader.y, frameHeader.w, frameHeader.h, frameHeader.comp);
+ //debugC(kDebugGraphics, "SpriteAsset::loadFrameHeader() stream = %d; x = %d; y = %d; w = %d; h = %d; comp = %d\n", frameHeader.stream, frameHeader.x, frameHeader.y, frameHeader.w, frameHeader.h, frameHeader.comp);
}
M4Sprite *SpriteAsset::getFrame(int frameIndex) {
if ((uint)frameIndex < _frames.size()) {
return _frames[frameIndex].frame;
} else {
- warning("SpriteAsset::getFrame: Invalid frame %d, out of %d", frameIndex, _frames.size());
+ debugC(kDebugGraphics, "SpriteAsset::getFrame: Invalid frame %d, out of %d", frameIndex, _frames.size());
return _frames[_frames.size() - 1].frame;
}
}
@@ -382,7 +384,7 @@ void SpriteAsset::loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX,
loadFrameHeader(frameHeader);
if (frameHeader.w > 0 && frameHeader.h > 0) {
- Common::MemoryReadStream *frameData = _stream->readStream(getFrameSize(frameIndex));
+ Common::SeekableReadStream *frameData = _stream->readStream(getFrameSize(frameIndex));
if (frameHeader.stream) {
frame->loadDeltaRle(frameData, destX - frameHeader.x, destY - frameHeader.y);
} else {
@@ -486,7 +488,7 @@ bool AssetManager::clearAssets(AssetType assetType, int32 minHash, int32 maxHash
bool AssetManager::loadAsset(const char *assetName, RGB8 *palette) {
- printf("AssetManager::loadAsset() %s\n", assetName);
+ debugC(kDebugGraphics, "AssetManager::loadAsset() %s\n", assetName);
// TODO, better use MemoryReadStreamEndian?
//convertAssetToLE(assetData, assetSize);
@@ -500,34 +502,34 @@ bool AssetManager::loadAsset(const char *assetName, RGB8 *palette) {
chunkSize = assetS->readUint32LE() - 12; // sub 12 for the chunk header
chunkHash = assetS->readUint32LE();
- printf("hash = %d\n", chunkHash);
+ debugC(kDebugGraphics, "hash = %d\n", chunkHash);
// Until loading code is complete, so that chunks not fully read are skipped correctly
uint32 nextOfs = assetS->pos() + chunkSize;
switch (chunkType) {
case CHUNK_MACH:
- printf("MACH\n");
+ debugC(kDebugGraphics, "MACH\n");
clearAssets(kAssetTypeMACH, chunkHash, chunkHash);
_MACH[chunkHash] = new MachineAsset(_vm, assetS, chunkSize, assetName);
break;
case CHUNK_SEQU:
- printf("SEQU\n");
+ debugC(kDebugGraphics, "SEQU\n");
clearAssets(kAssetTypeSEQU, chunkHash, chunkHash);
_SEQU[chunkHash] = new SequenceAsset(_vm, assetS, chunkSize, assetName);
break;
case CHUNK_DATA:
- printf("DATA\n");
+ debugC(kDebugGraphics, "DATA\n");
clearAssets(kAssetTypeDATA, chunkHash, chunkHash);
_DATA[chunkHash] = new DataAsset(_vm, assetS, chunkSize, assetName);
break;
case CHUNK_CELS:
- printf("CELS\n");
+ debugC(kDebugGraphics, "CELS\n");
clearAssets(kAssetTypeCELS, chunkHash, chunkHash);
_CELS[chunkHash] = new SpriteAsset(_vm, assetS, chunkSize, assetName);
break;
default:
- printf("AssetManager::loadAsset() Unknown chunk type %08X\n", chunkType);
+ debugC(kDebugGraphics, "AssetManager::loadAsset() Unknown chunk type %08X\n", chunkType);
}
// Until loading code is complete (see above)
@@ -568,7 +570,7 @@ int32 AssetManager::addSpriteAsset(const char *assetName, int32 hash, RGB8 *pale
if (!alreadyLoaded) {
- printf("AssetManager::addSpriteAsset() asset %s not loaded, loading into %d\n", assetName, hash);
+ debugC(kDebugGraphics, "AssetManager::addSpriteAsset() asset %s not loaded, loading into %d\n", assetName, hash);
clearAssets(kAssetTypeCELS, hash, hash);
@@ -578,7 +580,7 @@ int32 AssetManager::addSpriteAsset(const char *assetName, int32 hash, RGB8 *pale
} else {
- printf("AssetManager::addSpriteAsset() asset %s already loaded in %d\n", assetName, hash);
+ debugC(kDebugGraphics, "AssetManager::addSpriteAsset() asset %s already loaded in %d\n", assetName, hash);
/* TODO/FIXME
if (_CELS[hash].palOffset >= 0 && palette)
diff --git a/engines/m4/compression.cpp b/engines/m4/compression.cpp
index 2b3f4e1388..9b1416945c 100644
--- a/engines/m4/compression.cpp
+++ b/engines/m4/compression.cpp
@@ -26,6 +26,8 @@
#include "m4/compression.h"
#include "m4/m4.h"
+#include "common/memstream.h"
+
namespace M4 {
const char *madsPackString = "MADSPACK";
@@ -90,6 +92,10 @@ void MadsPack::initialise(Common::SeekableReadStream *stream) {
_dataOffset = stream->pos();
}
+Common::SeekableReadStream *MadsPack::getItemStream(int index) {
+ return new Common::MemoryReadStream(_items[index].data, _items[index].size, DisposeAfterUse::NO);
+}
+
MadsPack::~MadsPack() {
for (int i = 0; i < _count; ++i)
delete[] _items[i].data;
diff --git a/engines/m4/compression.h b/engines/m4/compression.h
index 00e3d1f927..93c7d9af9e 100644
--- a/engines/m4/compression.h
+++ b/engines/m4/compression.h
@@ -58,9 +58,7 @@ public:
int getCount() const { return _count; }
MadsPackEntry &getItem(int index) const { return _items[index]; }
MadsPackEntry &operator[](int index) const { return _items[index]; }
- Common::MemoryReadStream *getItemStream(int index) {
- return new Common::MemoryReadStream(_items[index].data, _items[index].size, DisposeAfterUse::NO);
- }
+ Common::SeekableReadStream *getItemStream(int index);
int getDataOffset() const { return _dataOffset; }
};
diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp
index 71c70e3e1b..88b4240901 100644
--- a/engines/m4/console.cpp
+++ b/engines/m4/console.cpp
@@ -61,8 +61,10 @@ static int strToInt(const char *s) {
return atoi(s);
// Hexadecimal string
- uint tmp;
- sscanf(s, "%xh", &tmp);
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
return (int)tmp;
}
@@ -121,7 +123,7 @@ bool Console::cmdPlaySound(int argc, const char **argv) {
bool Console::cmdPlayDSRSound(int argc, const char **argv) {
if (argc != 2 && argc != 3) {
DebugPrintf("Usage: %s <sound index> <DSR file>\n", argv[0]);
- DebugPrintf("The DSR file parameter is optional, and specifies which DSR to load\n", argv[0]);
+ DebugPrintf("The DSR file parameter is optional, and specifies which DSR to load\n");
} else {
if (argc == 3)
_vm->_sound->loadDSRFile(argv[2]);
@@ -319,17 +321,25 @@ bool MadsConsole::cmdMessage(int argc, const char **argv) {
DebugPrintf("message 'objnum'\n");
} else if (!strcmp(argv[1], "list_quotes")) {
// Dump the quotes list
+#if 0
+ // FIXME: The following code is not portable and hence has been disabled.
+ // Try replacing FILE by Common::DumpFile.
FILE *destFile = fopen("mads_quotes.txt", "wb");
for (uint i = 0; i < _vm->globals()->getQuotesSize(); ++i)
fprintf(destFile, "%.3d - %s\n", i, _vm->globals()->getQuote(i));
fclose(destFile);
+#endif
} else if (!strcmp(argv[1], "list_vocab")) {
// Dump the vocab list
+#if 0
+ // FIXME: The following code is not portable and hence has been disabled.
+ // Try replacing FILE by Common::DumpFile.
FILE *destFile = fopen("mads_vocab.txt", "wb");
for (uint i = 1; i <= _vm->globals()->getVocabSize(); ++i)
fprintf(destFile, "%.3d/%.3x - %s\n", i, i, _vm->globals()->getVocab(i));
fclose(destFile);
+#endif
} else {
int messageIdx = strToInt(argv[1]);
diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp
index af26a86313..70c30d28f2 100644
--- a/engines/m4/converse.cpp
+++ b/engines/m4/converse.cpp
@@ -25,6 +25,7 @@
#include "common/array.h"
#include "common/hashmap.h"
+#include "common/substream.h"
#include "m4/converse.h"
#include "m4/resource.h"
@@ -118,7 +119,7 @@ void ConversationView::setNode(int32 nodeIndex) {
_activeItems.push_back(node->entries[i]);
if (node->entries[i]->autoSelect || strlen(node->entries[i]->text) == 0) {
- //printf("Auto selecting entry %i of node %i\n", i, nodeIndex);
+ //warning(kDebugConversations, "Auto selecting entry %i of node %i\n", i, nodeIndex);
selectEntry(i);
return;
}
@@ -134,11 +135,11 @@ void ConversationView::setNode(int32 nodeIndex) {
// Fallthrough
if (node->fallthroughMinEntries >= 0 && node->fallthroughOffset >= 0) {
- //printf("Current node falls through node at offset %i when entries are less or equal than %i\n",
+ //warning(kDebugConversations, "Current node falls through node at offset %i when entries are less or equal than %i\n",
// node->fallthroughOffset, node->fallthroughMinEntries);
if (_activeItems.size() <= (uint32)node->fallthroughMinEntries) {
const EntryInfo *entryInfo = _m4Vm->_converse->getEntryInfo(node->fallthroughOffset);
- //printf("Entries are less than or equal to %i, falling through to node at offset %i, index %i\n",
+ //warning(kDebugConversations, "Entries are less than or equal to %i, falling through to node at offset %i, index %i\n",
// node->fallthroughMinEntries, node->fallthroughOffset, entryInfo->nodeIndex);
setNode(entryInfo->nodeIndex);
return;
@@ -227,10 +228,10 @@ void ConversationView::selectEntry(int entryIndex) {
// Hide selected entry, unless it has a persistent flag set
if (!(_activeItems[entryIndex]->flags & kEntryPersists)) {
- //printf("Hiding selected entry\n");
+ //debugCN(kDebugConversations, "Hiding selected entry\n");
_m4Vm->_converse->getNode(_currentNodeIndex)->entries[entryIndex]->visible = false;
} else {
- //printf("Selected entry is persistent, not hiding it\n");
+ //debugCN(kDebugConversations, "Selected entry is persistent, not hiding it\n");
}
}
@@ -286,7 +287,7 @@ void ConversationView::playNextReply() {
}
} else {
int selectedWeight = _vm->_random->getRandomNumber(currentEntry->totalWeight - 1) + 1;
- //printf("Selected weight: %i\n", selectedWeight);
+ //debugCN(kDebugConversations, "Selected weight: %i\n", selectedWeight);
int previousWeight = 1;
int currentWeight = 0;
@@ -313,7 +314,7 @@ void ConversationView::playNextReply() {
// If we reached here, there are no more replies, so perform the active entry's actions
- //printf("Current selection does %i actions\n", _activeItems[entryIndex]->actions.size());
+ //debugCN(kDebugConversations, "Current selection does %i actions\n", _activeItems[entryIndex]->actions.size());
for (uint32 i = 0; i < _activeItems[_highlightedIndex]->actions.size(); i++) {
if (!_m4Vm->_converse->performAction(_activeItems[_highlightedIndex]->actions[i]))
break;
@@ -399,30 +400,30 @@ void Converse::loadConversation(const char *convName) {
return;
}
size = convS->readUint32LE(); // is this used at all?
- if (debugFlag) printf("Conv chunk size (external header): %i\n", size);
+ if (debugFlag) debugCN(kDebugConversations, "Conv chunk size (external header): %i\n", size);
// Conversation name, which is the conversation file's name
// without the extension
convS->read(buffer, 8);
- if (debugFlag) printf("Conversation name: %s\n", buffer);
+ if (debugFlag) debugCN(kDebugConversations, "Conversation name: %s\n", buffer);
while (true) {
chunkPos = convS->pos();
chunk = convS->readUint32LE(); // read chunk
if (convS->eos()) break;
- if (debugFlag) printf("***** Pos: %i -> ", chunkPos);
+ if (debugFlag) debugC(kDebugConversations, "***** Pos: %i -> ", chunkPos);
switch (chunk) {
case CHUNK_DECL: // Declare
- if (debugFlag) printf("DECL chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "DECL chunk\n");
data = convS->readUint32LE();
- if (debugFlag) printf("Tag: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Tag: %i\n", data);
if (data > 0)
warning("Tag > 0 in DECL chunk, value is: %i", data); // TODO
val = convS->readUint32LE();
- if (debugFlag) printf("Value: %i\n", val);
+ if (debugFlag) debugCN(kDebugConversations, "Value: %i\n", val);
data = convS->readUint32LE();
- if (debugFlag) printf("Flags: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Flags: %i\n", data);
if (data > 0)
warning("Flags != 0 in DECL chunk, value is %i", data); // TODO
setValue(chunkPos, val);
@@ -437,23 +438,23 @@ void Converse::loadConversation(const char *convName) {
curEntry->fallthroughMinEntries = -1;
curEntry->fallthroughOffset = -1;
if (chunk == CHUNK_NODE) {
- if (debugFlag) printf("NODE chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "NODE chunk\n");
} else {
- if (debugFlag) printf("LNOD chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "LNOD chunk\n");
}
curNode = convS->readUint32LE();
- if (debugFlag) printf("Node number: %i\n", curNode);
+ if (debugFlag) debugCN(kDebugConversations, "Node number: %i\n", curNode);
data = convS->readUint32LE();
- if (debugFlag) printf("Tag: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Tag: %i\n", data);
if (chunk == CHUNK_LNOD) {
autoSelectIndex = convS->readUint32LE();
- if (debugFlag) printf("Autoselect entry number: %i\n", autoSelectIndex);
+ if (debugFlag) debugCN(kDebugConversations, "Autoselect entry number: %i\n", autoSelectIndex);
}
size = convS->readUint32LE();
- if (debugFlag) printf("Number of entries: %i\n", size);
+ if (debugFlag) debugCN(kDebugConversations, "Number of entries: %i\n", size);
for (i = 0; i < size; i++) {
data = convS->readUint32LE();
- if (debugFlag) printf("Entry %i: %i\n", i + 1, data);
+ if (debugFlag) debugCN(kDebugConversations, "Entry %i: %i\n", i + 1, data);
}
_convNodes.push_back(curEntry);
setEntryInfo(curEntry->offset, curEntry->entryType, curNode, -1);
@@ -465,14 +466,14 @@ void Converse::loadConversation(const char *convName) {
curEntry->entryType = kEntry;
strcpy(curEntry->voiceFile, "");
strcpy(curEntry->text, "");
- if (debugFlag) printf("ETRY chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "ETRY chunk\n");
data = convS->readUint32LE();
- if (debugFlag) printf("Unknown (attributes perhaps?): %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Unknown (attributes perhaps?): %i\n", data);
data = convS->readUint32LE();
- if (debugFlag) printf("Entry flags: ");
- if (debugFlag) if (data & kEntryInitial) printf ("Initial ");
- if (debugFlag) if (data & kEntryPersists) printf ("Persists ");
- if (debugFlag) printf("\n");
+ if (debugFlag) debugCN(kDebugConversations, "Entry flags: ");
+ if (debugFlag) if (data & kEntryInitial) debugCN(kDebugConversations, "Initial ");
+ if (debugFlag) if (data & kEntryPersists) debugCN(kDebugConversations, "Persists ");
+ if (debugFlag) debugCN(kDebugConversations, "\n");
curEntry->flags = data;
curEntry->visible = (curEntry->flags & kEntryInitial) ? true : false;
if (autoSelectIndex >= 0) {
@@ -517,30 +518,33 @@ void Converse::loadConversation(const char *convName) {
if (chunk == CHUNK_WPRL || chunk == CHUNK_WRPL) {
replyEntry->entryType = kWeightedReply;
replyEntry->totalWeight = 0;
- if (debugFlag) printf("WRPL chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "WRPL chunk\n");
size = convS->readUint32LE();
- if (debugFlag) printf("Weighted reply %i - %i entries:\n", i, size);
+ if (debugFlag) debugCN(kDebugConversations, "Weighted reply %i - %i entries:\n", i, size);
for (i = 0; i < size; i++) {
weightedEntry = new ConvEntry();
weightedEntry->offset = chunkPos;
strcpy(weightedEntry->voiceFile, "");
weightedEntry->entryType = kWeightedReply;
data = convS->readUint32LE();
- if (debugFlag) printf("- Weight: %i ", data);
+ if (debugFlag) debugCN(kDebugConversations, "- Weight: %i ", data);
weightedEntry->weight = data;
replyEntry->totalWeight += weightedEntry->weight;
data = convS->readUint32LE();
- if (debugFlag) printf("offset: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "offset: %i\n", data);
replyEntry->entries.push_back(weightedEntry);
}
currentWeightedEntry = 0;
} else {
replyEntry->entryType = kReply;
- if (debugFlag) printf("RPLY chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "RPLY chunk\n");
data = convS->readUint32LE();
- if (debugFlag) printf("Reply data offset: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Reply data offset: %i\n", data);
}
+ if (!curEntry)
+ error("Converse::loadConversation(): curEntry is NULL while adding a reply");
+
curEntry->entries.push_back(replyEntry);
setEntryInfo(replyEntry->offset, replyEntry->entryType,
curNode, _convNodes[curNode]->entries.size() - 1);
@@ -560,9 +564,9 @@ void Converse::loadConversation(const char *convName) {
{
ConvEntry* parentEntry = NULL;
if (chunk == CHUNK_TEXT) {
- if (debugFlag) printf("TEXT chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "TEXT chunk\n");
} else {
- if (debugFlag) printf("MESG chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "MESG chunk\n");
}
if (replyEntry == NULL) {
@@ -572,25 +576,27 @@ void Converse::loadConversation(const char *convName) {
} else if (replyEntry != NULL && replyEntry->entryType == kWeightedReply) {
parentEntry = replyEntry->entries[currentWeightedEntry];
currentWeightedEntry++;
+ } else {
+ error("Converse::loadConversation(): Unexpected reply entry while processing TEXT/MESG chunk");
}
size = convS->readUint32LE();
- if (debugFlag) printf("Entry data size: %i\n", size);
+ if (debugFlag) debugCN(kDebugConversations, "Entry data size: %i\n", size);
convS->read(buffer, 8);
size -= 8; // subtract maximum length of voice file name
// If the file name is 8 characters, it will not be 0-terminated, so use strncpy
strncpy(parentEntry->voiceFile, buffer, 8);
parentEntry->voiceFile[8] = '\0';
- if (debugFlag) printf("Voice file: %s\n", parentEntry->voiceFile);
+ if (debugFlag) debugCN(kDebugConversations, "Voice file: %s\n", parentEntry->voiceFile);
if (chunk == CHUNK_TEXT) {
convS->read(buffer, size);
- if (debugFlag) printf("Text: %s\n", buffer);
+ if (debugFlag) debugCN(kDebugConversations, "Text: %s\n", buffer);
sprintf(parentEntry->text, "%s", buffer);
} else {
while (size > 0) {
data = convS->readUint32LE();
- if (debugFlag) printf("Unknown: %i\n", data); // TODO
+ if (debugFlag) debugCN(kDebugConversations, "Unknown: %i\n", data); // TODO
size -= 4;
}
}
@@ -607,7 +613,7 @@ void Converse::loadConversation(const char *convName) {
case CHUNK_CASN: // Conditional assign
case CHUNK_ASGN: // Assign
curAction = new EntryAction();
- if (debugFlag) printf("ASGN chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "ASGN chunk\n");
curAction->actionType = kAssignValue;
// Conditional part
@@ -650,32 +656,32 @@ void Converse::loadConversation(const char *convName) {
if (chunk == CHUNK_GOTO || chunk == CHUNK_CCGO) {
curAction->actionType = kGotoEntry;
- if (debugFlag) printf("GOTO chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "GOTO chunk\n");
} else if (chunk == CHUNK_HIDE || chunk == CHUNK_CHDE) {
curAction->actionType = kHideEntry;
- if (debugFlag) printf("HIDE chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "HIDE chunk\n");
} else if (chunk == CHUNK_UHID || chunk == CHUNK_CUHD) {
curAction->actionType = kUnhideEntry;
- if (debugFlag) printf("UHID chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "UHID chunk\n");
} else if (chunk == CHUNK_DSTR || chunk == CHUNK_CDST) {
curAction->actionType = kDestroyEntry;
- if (debugFlag) printf("DSTR chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "DSTR chunk\n");
} else if (chunk == CHUNK_EXIT || chunk == CHUNK_CEGO) {
curAction->actionType = kExitConv;
- if (debugFlag) printf("EXIT chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "EXIT chunk\n");
}
data = convS->readUint32LE();
- if (debugFlag) printf("Offset: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Offset: %i\n", data);
curAction->targetOffset = data;
curEntry->actions.push_back(curAction);
break;
case CHUNK_FALL: // Fallthrough
- if (debugFlag) printf("FALL chunk\n");
+ if (debugFlag) debugCN(kDebugConversations, "FALL chunk\n");
size = convS->readUint32LE();
- if (debugFlag) printf("Minimum nodes: %i\n", size);
+ if (debugFlag) debugCN(kDebugConversations, "Minimum nodes: %i\n", size);
_convNodes[curNode]->fallthroughMinEntries = size;
data = convS->readUint32LE();
- if (debugFlag) printf("Offset: %i\n", data);
+ if (debugFlag) debugCN(kDebugConversations, "Offset: %i\n", data);
_convNodes[curNode]->fallthroughOffset = data;
break;
// ----------------------------------------------------------------------------
@@ -713,35 +719,35 @@ void Converse::loadConversationMads(const char *convName) {
// ------------------------------------------------------------
// Chunk 0
convS = convDataD.getItemStream(0);
- printf("Chunk 0\n");
- printf("Conv stream size: %i\n", convS->size());
+ debugCN(kDebugConversations, "Chunk 0\n");
+ debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
while (!convS->eos()) { // FIXME (eos changed)
- printf("%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
// ------------------------------------------------------------
// Chunk 1
convS = convDataD.getItemStream(1);
- printf("Chunk 1\n");
- printf("Conv stream size: %i\n", convS->size());
+ debugCN(kDebugConversations, "Chunk 1\n");
+ debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
while (!convS->eos()) { // FIXME (eos changed)
- printf("%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
// ------------------------------------------------------------
// Chunk 2
convS = convDataD.getItemStream(2);
- printf("Chunk 2\n");
- printf("Conv stream size: %i\n", convS->size());
+ debugCN(kDebugConversations, "Chunk 2\n");
+ debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
while (!convS->eos()) { // FIXME (eos changed)
- printf("%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
// ------------------------------------------------------------
// CNV file
@@ -764,46 +770,46 @@ void Converse::loadConversationMads(const char *convName) {
// TODO: finish this
// Chunk 0
convS = convData.getItemStream(0);
- printf("Chunk 0\n");
- printf("Conv stream size: %i\n", convS->size());
- printf("%i ", convS->readUint16LE());
- printf("%i ", convS->readUint16LE());
- printf("%i ", convS->readUint16LE());
- printf("%i ", convS->readUint16LE());
- printf("%i ", convS->readUint16LE());
- printf("%i ", convS->readUint16LE());
- printf("\n");
+ debugCN(kDebugConversations, "Chunk 0\n");
+ debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "\n");
count = convS->readUint16LE(); // conversation face count (usually 2)
- printf("Conversation faces: %i\n", count);
+ debugCN(kDebugConversations, "Conversation faces: %i\n", count);
for (i = 0; i < 5; i++) {
convS->read(buffer, 16);
- printf("Face %i: %s ", i + 1, buffer);
+ debugCN(kDebugConversations, "Face %i: %s ", i + 1, buffer);
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
// 5 face slots
// 1 = face slot has a face (with the filename specified above)
// 0 = face slot doesn't contain face data
for (i = 0; i < 5; i++) {
- printf("%i ", convS->readUint16LE());
+ debugCN(kDebugConversations, "%i ", convS->readUint16LE());
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
convS->read(buffer, 14); // speech file
- printf("Speech file: %s\n", buffer);
+ debugCN(kDebugConversations, "Speech file: %s\n", buffer);
while (!convS->eos()) { // FIXME: eos changed
- printf("%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
delete convS;
// ------------------------------------------------------------
// Chunk 1: Conversation nodes
convS = convData.getItemStream(1);
- printf("Chunk 1: conversation nodes\n");
- printf("Conv stream size: %i\n", convS->size());
+ debugCN(kDebugConversations, "Chunk 1: conversation nodes\n");
+ debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
while (true) {
uint16 id = convS->readUint16LE();
@@ -823,12 +829,12 @@ void Converse::loadConversationMads(const char *convName) {
unk = convS->readUint16LE();
assert (unk == 65535);
_convNodes.push_back(curEntry);
- printf("Node %i, ID %i, entries %i\n", _convNodes.size(), curEntry->id, curEntry->entryCount);
+ debugCN(kDebugConversations, "Node %i, ID %i, entries %i\n", _convNodes.size(), curEntry->id, curEntry->entryCount);
// flags = 0: node has more than 1 entry
// flags = 65535: node has 1 entry
}
- printf("Conversation has %i nodes\n", _convNodes.size());
- printf("\n");
+ debugCN(kDebugConversations, "Conversation has %i nodes\n", _convNodes.size());
+ debugCN(kDebugConversations, "\n");
delete convS;
@@ -839,14 +845,14 @@ void Converse::loadConversationMads(const char *convName) {
// ------------------------------------------------------------
// Chunk 5 contains the conversation strings
convS = convData.getItemStream(5);
- //printf("Chunk 5: conversation strings\n");
- //printf("Conv stream size: %i\n", convS->size());
+ //debugCN(kDebugConversations, "Chunk 5: conversation strings\n");
+ //debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
*buffer = 0;
while (true) {
//if (curPos == 0)
- // printf("%i: Offset %i: ", _convStrings.size(), convS->pos());
+ // debugCN(kDebugConversations, "%i: Offset %i: ", _convStrings.size(), convS->pos());
uint8 b = convS->readByte();
if (convS->eos()) break;
@@ -857,7 +863,7 @@ void Converse::loadConversationMads(const char *convName) {
}
if (buffer[curPos - 1] == '\0') {
// end of string
- //printf("%s\n", buffer);
+ //debugCN(kDebugConversations, "%s\n", buffer);
buf = new char[strlen(buffer) + 1];
strcpy(buf, buffer);
_convStrings.push_back(buf);
@@ -871,8 +877,8 @@ void Converse::loadConversationMads(const char *convName) {
// ------------------------------------------------------------
// Chunk 2: entry data
convS = convData.getItemStream(2);
- //printf("Chunk 2 - entry data\n");
- //printf("Conv stream size: %i\n", convS->size());
+ //debugCN(kDebugConversations, "Chunk 2 - entry data\n");
+ //debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
for (i = 0; i < _convNodes.size(); i++) {
for (j = 0; j < _convNodes[i]->entryCount; j++) {
@@ -887,7 +893,7 @@ void Converse::loadConversationMads(const char *convName) {
curEntry->size = convS->readUint16LE();
_convNodes[i]->entries.push_back(curEntry);
- //printf("Node %i, entry %i, id %i, offset %i, size %i, text: %s\n",
+ //debugCN(kDebugConversations, "Node %i, entry %i, id %i, offset %i, size %i, text: %s\n",
// i, j, curEntry->id, curEntry->offset, curEntry->size, curEntry->text);
}
}
@@ -897,8 +903,8 @@ void Converse::loadConversationMads(const char *convName) {
// ------------------------------------------------------------
// Chunk 3: message (MESG) chunks, created from the strings of chunk 5
convS = convData.getItemStream(3);
- //printf("Chunk 3 - MESG chunk data\n");
- //printf("Conv stream size: %i\n", convS->size());
+ //debugCN(kDebugConversations, "Chunk 3 - MESG chunk data\n");
+ //debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
while (true) {
uint16 index = convS->readUint16LE();
@@ -908,15 +914,15 @@ void Converse::loadConversationMads(const char *convName) {
stringIndex = index;
stringCount = convS->readUint16LE();
*buffer = 0;
- //printf("Message: %i\n", _madsMessageList.size());
+ //debugCN(kDebugConversations, "Message: %i\n", _madsMessageList.size());
for (i = stringIndex; i < stringIndex + stringCount; i++) {
- //printf("%i: %s\n", i, _convStrings[i]);
+ //debugCN(kDebugConversations, "%i: %s\n", i, _convStrings[i]);
curMessage->messageStrings.push_back(_convStrings[i]);
}
_madsMessageList.push_back(curMessage);
- //printf("----------\n");
+ //debugCN(kDebugConversations, "----------\n");
}
- //printf("\n");
+ //debugCN(kDebugConversations, "\n");
delete convS;
@@ -924,38 +930,38 @@ void Converse::loadConversationMads(const char *convName) {
// TODO: finish this
// Chunk 6: conversation script
convS = convData.getItemStream(6);
- printf("Chunk 6\n");
- printf("Conv stream size: %i\n", convS->size());
+ debugCN(kDebugConversations, "Chunk 6\n");
+ debugCN(kDebugConversations, "Conv stream size: %i\n", convS->size());
/*while (!convS->eos()) { // FIXME (eos changed)
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("%i ", convS->readByte());
- printf("\n");
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "%i ", convS->readByte());
+ debugCN(kDebugConversations, "\n");
}
return;*/
for (i = 0; i < _convNodes.size(); i++) {
for (j = 0; j < _convNodes[i]->entryCount; j++) {
- printf("*** Node %i entry %i data size %i\n", i, j, _convNodes[i]->entries[j]->size);
- printf("Entry ID %i, text %s\n", _convNodes[i]->entries[j]->id, _convNodes[i]->entries[j]->text);
- Common::SubReadStream *entryStream = new Common::SubReadStream(convS, _convNodes[i]->entries[j]->size);
+ debugCN(kDebugConversations, "*** Node %i entry %i data size %i\n", i, j, _convNodes[i]->entries[j]->size);
+ debugCN(kDebugConversations, "Entry ID %i, text %s\n", _convNodes[i]->entries[j]->id, _convNodes[i]->entries[j]->text);
+ Common::ReadStream *entryStream = new Common::SubReadStream(convS, _convNodes[i]->entries[j]->size);
readConvEntryActions(entryStream, _convNodes[i]->entries[j]);
delete entryStream;
- printf("--------------------\n");
+ debugCN(kDebugConversations, "--------------------\n");
}
}
delete convS;
}
-void Converse::readConvEntryActions(Common::SubReadStream *convS, ConvEntry *curEntry) {
+void Converse::readConvEntryActions(Common::ReadStream *convS, ConvEntry *curEntry) {
uint8 chunk;
uint8 type; // 255: normal, 11: conditional
uint8 hasText1, hasText2;
@@ -973,50 +979,50 @@ void Converse::readConvEntryActions(Common::SubReadStream *convS, ConvEntry *cur
switch (chunk) {
case 1:
- printf("TODO: chunk type %i\n", chunk);
+ debugCN(kDebugConversations, "TODO: chunk type %i\n", chunk);
break;
case 2:
- printf("HIDE\n");
+ debugCN(kDebugConversations, "HIDE\n");
convS->readByte();
count = convS->readByte();
- printf("%i entries: ", count);
+ debugCN(kDebugConversations, "%i entries: ", count);
for (int i = 0; i < count; i++)
- printf("%i %d", i, convS->readUint16LE());
- printf("\n");
+ debugCN(kDebugConversations, "%i %d", i, convS->readUint16LE());
+ debugCN(kDebugConversations, "\n");
break;
case 3:
- printf("UNHIDE\n");
+ debugCN(kDebugConversations, "UNHIDE\n");
convS->readByte();
count = convS->readByte();
- printf("%i entries: ", count);
+ debugCN(kDebugConversations, "%i entries: ", count);
for (int i = 0; i < count; i++)
- printf("%i %d", i, convS->readUint16LE());
- printf("\n");
+ debugCN(kDebugConversations, "%i %d", i, convS->readUint16LE());
+ debugCN(kDebugConversations, "\n");
break;
case 4: // MESSAGE
- printf("MESSAGE\n");
+ debugCN(kDebugConversations, "MESSAGE\n");
if (type == 255) {
- //printf("unconditional\n");
+ //debugCN(kDebugConversations, "unconditional\n");
} else if (type == 11) {
- //printf("conditional\n");
+ //debugCN(kDebugConversations, "conditional\n");
} else {
- printf("unknown type: %i\n", type);
+ debugCN(kDebugConversations, "unknown type: %i\n", type);
}
// Conditional part
if (type == 11) {
unk = convS->readUint16LE(); // 1
if (unk != 1)
- printf("Message: unk != 1 (it's %i)\n", unk);
+ debugCN(kDebugConversations, "Message: unk != 1 (it's %i)\n", unk);
var = convS->readUint16LE();
val = convS->readUint16LE();
- printf("Var %i == %i\n", var, val);
+ debugCN(kDebugConversations, "Var %i == %i\n", var, val);
}
unk = convS->readUint16LE(); // 256
if (unk != 256)
- printf("Message: unk != 256 (it's %i)\n", unk);
+ debugCN(kDebugConversations, "Message: unk != 256 (it's %i)\n", unk);
// it seems that the first text entry is set when the message
// chunk is supposed to be shown unconditionally, whereas the second text
@@ -1026,74 +1032,74 @@ void Converse::readConvEntryActions(Common::SubReadStream *convS, ConvEntry *cur
if (hasText1 == 1) {
messageIndex = convS->readUint16LE();
- printf("Message 1 index: %i, text:\n", messageIndex);
+ debugCN(kDebugConversations, "Message 1 index: %i, text:\n", messageIndex);
for (uint32 i = 0; i < _madsMessageList[messageIndex]->messageStrings.size(); i++) {
- printf("%s\n", _madsMessageList[messageIndex]->messageStrings[i]);
+ debugCN(kDebugConversations, "%s\n", _madsMessageList[messageIndex]->messageStrings[i]);
}
}
if (hasText2 == 1) {
messageIndex = convS->readUint16LE();
if (hasText1 == 0) {
- printf("Message 2 index: %i, text:\n", messageIndex);
+ debugCN(kDebugConversations, "Message 2 index: %i, text:\n", messageIndex);
for (uint32 i = 0; i < _madsMessageList[messageIndex]->messageStrings.size(); i++) {
- printf("%s\n", _madsMessageList[messageIndex]->messageStrings[i]);
+ debugCN(kDebugConversations, "%s\n", _madsMessageList[messageIndex]->messageStrings[i]);
}
}
}
break;
case 5: // AUTO
- printf("AUTO\n");
+ debugCN(kDebugConversations, "AUTO\n");
for (int k = 0; k < 4; k++)
convS->readByte();
messageIndex = convS->readUint16LE();
- printf("Message index: %i, text:\n", messageIndex);
+ debugCN(kDebugConversations, "Message index: %i, text:\n", messageIndex);
for (uint32 i = 0; i < _madsMessageList[messageIndex]->messageStrings.size(); i++) {
- printf("%s\n", _madsMessageList[messageIndex]->messageStrings[i]);
+ debugCN(kDebugConversations, "%s\n", _madsMessageList[messageIndex]->messageStrings[i]);
}
convS->readUint16LE();
break;
case 6:
- printf("TODO: chunk type %i\n", chunk);
+ debugCN(kDebugConversations, "TODO: chunk type %i\n", chunk);
break;
case 7: // GOTO
unk = convS->readUint32LE(); // 0
if (unk != 0 && unk != 1)
- printf("Goto: unk != 0 or 1 (it's %i)\n", unk);
+ debugCN(kDebugConversations, "Goto: unk != 0 or 1 (it's %i)\n", unk);
target = convS->readUint16LE();
convS->readUint16LE(); // 255
if (unk != 0)
- printf("Goto: unk != 0 (it's %i)\n", unk);
+ debugCN(kDebugConversations, "Goto: unk != 0 (it's %i)\n", unk);
if (target != 65535)
- printf("GOTO node %i\n", target);
+ debugCN(kDebugConversations, "GOTO node %i\n", target);
else
- printf("GOTO exit\n");
+ debugCN(kDebugConversations, "GOTO exit\n");
break;
case 8:
- printf("TODO: chunk type %i\n", chunk);
+ debugCN(kDebugConversations, "TODO: chunk type %i\n", chunk);
break;
case 9: // ASSIGN
- //printf("ASSIGN\n");
+ //debugCN(kDebugConversations, "ASSIGN\n");
unk = convS->readUint32LE(); // 0
if (unk != 0)
- printf("Assign: unk != 0 (it's %i)\n", unk);
+ debugCN(kDebugConversations, "Assign: unk != 0 (it's %i)\n", unk);
val = convS->readUint16LE();
var = convS->readUint16LE();
- //printf("Var %i = %i\n", var, val);
+ //debugCN(kDebugConversations, "Var %i = %i\n", var, val);
break;
default:
- printf ("Unknown chunk type! (%i)\n", chunk);
+ debugCN(kDebugConversations, "Unknown chunk type! (%i)\n", chunk);
break;
}
}
- printf("\n");
+ debugCN(kDebugConversations, "\n");
}
void Converse::setEntryInfo(int32 offset, EntryType type, int32 nodeIndex, int32 entryIndex) {
@@ -1104,7 +1110,7 @@ void Converse::setEntryInfo(int32 offset, EntryType type, int32 nodeIndex, int32
info.nodeIndex = nodeIndex;
info.entryIndex = entryIndex;
_offsetMap[hashOffset] = info;
- //printf("Set entry info: offset %i, type %i, node %i, entry %i\n", offset, type, nodeIndex, entryIndex);
+ //debugCN(kDebugConversations, "Set entry info: offset %i, type %i, node %i, entry %i\n", offset, type, nodeIndex, entryIndex);
}
const EntryInfo* Converse::getEntryInfo(int32 offset) {
@@ -1167,7 +1173,7 @@ bool Converse::performAction(EntryAction *action) {
}
if (action->actionType == kAssignValue) {
- //printf("Assigning variable at offset %i to value %i\n",
+ //debugCN(kDebugConversations, "Assigning variable at offset %i to value %i\n",
// action->targetOffset, action->value);
setValue(action->targetOffset, action->value);
return true; // nothing else to do in an assignment action
@@ -1185,24 +1191,24 @@ bool Converse::performAction(EntryAction *action) {
switch (action->actionType) {
case kGotoEntry:
- //printf("Goto entry at offset %i. Associated node is %i, entry %i\n",
+ //debugCN(kDebugConversations, "Goto entry at offset %i. Associated node is %i, entry %i\n",
// action->targetOffset, entryInfo->nodeIndex, entryInfo->entryIndex);
_vm->_conversationView->setNode(entryInfo->nodeIndex);
if (entryInfo->entryIndex >= 0)
_vm->_conversationView->selectEntry(entryInfo->entryIndex);
return false;
case kHideEntry:
- //printf("Hide entry at offset %i. Associated node is %i, entry %i\n",
+ //debugCN(kDebugConversations, "Hide entry at offset %i. Associated node is %i, entry %i\n",
// targetEntry->offset, entryInfo->nodeIndex, entryInfo->entryIndex);
targetEntry->visible = false;
return true;
case kUnhideEntry:
- //printf("Show entry at offset %i. Associated node is %i, entry %i\n",
+ //debugCN(kDebugConversations, "Show entry at offset %i. Associated node is %i, entry %i\n",
// targetEntry->offset, entryInfo->nodeIndex, entryInfo->entryIndex);
targetEntry->visible = true;
return true;
case kDestroyEntry:
- //printf("Destroy entry at offset %i. Associated node is %i, entry %i\n",
+ //debugCN(kDebugConversations, "Destroy entry at offset %i. Associated node is %i, entry %i\n",
// targetEntry->offset, entryInfo->nodeIndex, entryInfo->entryIndex);
if (entryInfo->entryIndex >= 0)
getNode(entryInfo->nodeIndex)->entries.remove_at(entryInfo->entryIndex);
@@ -1211,7 +1217,7 @@ bool Converse::performAction(EntryAction *action) {
targetEntry->visible = true;
return true;
case kExitConv:
- //printf("Exit conversation\n");
+ //debugCN(kDebugConversations, "Exit conversation\n");
endConversation();
return false;
default:
diff --git a/engines/m4/converse.h b/engines/m4/converse.h
index 609711b10f..67ecf20424 100644
--- a/engines/m4/converse.h
+++ b/engines/m4/converse.h
@@ -191,7 +191,7 @@ private:
void loadConversation(const char *convName);
void loadConversationMads(const char *convName);
- void readConvEntryActions(Common::SubReadStream *convS, ConvEntry *curEntry);
+ void readConvEntryActions(Common::ReadStream *convS, ConvEntry *curEntry);
void setEntryInfo(int32 offset, EntryType type, int32 nodeIndex, int32 entryIndex);
};
diff --git a/engines/m4/dialogs.cpp b/engines/m4/dialogs.cpp
index a7104537f5..101f21f48c 100644
--- a/engines/m4/dialogs.cpp
+++ b/engines/m4/dialogs.cpp
@@ -297,7 +297,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v
char *lineP = &dialogLine[0];
char *cmdP = NULL;
- while (*(srcP - 1) != '\0') {
+ while (srcP && *(srcP - 1) != '\0') {
if ((*srcP == '\n') || (*srcP == '\0')) {
// Line completed
*lineP = '\0';
diff --git a/engines/m4/events.cpp b/engines/m4/events.cpp
index c66609844a..29b243aceb 100644
--- a/engines/m4/events.cpp
+++ b/engines/m4/events.cpp
@@ -235,7 +235,7 @@ bool Mouse::init(const char *seriesName, RGB8 *palette) {
cursorPalette = _cursorSprites->getPalette();
_vm->_palette->setPalette(cursorPalette, 0, colorCount);
- //printf("Cursor count: %d\n", _cursorSprites->getCount());
+ //debugCN(kDebugCore, "Cursor count: %d\n", _cursorSprites->getCount());
_vm->res()->toss(seriesName);
diff --git a/engines/m4/font.cpp b/engines/m4/font.cpp
index b5965732e5..cba32c2225 100644
--- a/engines/m4/font.cpp
+++ b/engines/m4/font.cpp
@@ -82,7 +82,7 @@ void Font::setFontM4(const char *filename) {
Common::SeekableReadStream *fontFile = _vm->res()->openFile(filename);
if (fontFile->readUint32LE() != MKID_BE('FONT')) {
- printf("Font::Font: FONT tag expected\n");
+ debugCN(kDebugGraphics, "Font::Font: FONT tag expected\n");
return;
}
@@ -90,10 +90,10 @@ void Font::setFontM4(const char *filename) {
_maxWidth = fontFile->readByte();
uint32 fontSize = fontFile->readUint32LE();
- //printf("Font::Font: _maxWidth = %d, _maxHeight = %d, fontSize = %d\n", _maxWidth, _maxHeight, fontSize);
+ //debugCN(kDebugGraphics, "Font::Font: _maxWidth = %d, _maxHeight = %d, fontSize = %d\n", _maxWidth, _maxHeight, fontSize);
if (fontFile->readUint32LE() != MKID_BE('WIDT')) {
- printf("Font::Font: WIDT tag expected\n");
+ debugCN(kDebugGraphics, "Font::Font: WIDT tag expected\n");
return;
}
@@ -101,7 +101,7 @@ void Font::setFontM4(const char *filename) {
fontFile->read(_charWidths, 256);
if (fontFile->readUint32LE() != MKID_BE('OFFS')) {
- printf("Font::Font: OFFS tag expected\n");
+ debugCN(kDebugGraphics, "Font::Font: OFFS tag expected\n");
return;
}
@@ -111,7 +111,7 @@ void Font::setFontM4(const char *filename) {
_charOffs[i] = fontFile->readUint16LE();
if (fontFile->readUint32LE() != MKID_BE('PIXS')) {
- printf("Font::Font: PIXS tag expected\n");
+ debugCN(kDebugGraphics, "Font::Font: PIXS tag expected\n");
return;
}
diff --git a/engines/m4/globals.cpp b/engines/m4/globals.cpp
index a96229a0b3..d982ecad0e 100644
--- a/engines/m4/globals.cpp
+++ b/engines/m4/globals.cpp
@@ -75,7 +75,7 @@ bool Kernel::sendTrigger(int32 triggerNum) {
bool Kernel::handleTrigger(int32 triggerNum) {
- printf("betweenRooms = %d; triggerNum = %08X\n", betweenRooms, (uint)triggerNum);
+ debugCN(kDebugScript, "betweenRooms = %d; triggerNum = %08X\n", betweenRooms, (uint)triggerNum);
if (betweenRooms)
return true;
@@ -89,10 +89,10 @@ bool Kernel::handleTrigger(int32 triggerNum) {
int room = (triggerNum >> 16) & 0xFFF;
- printf("room = %d; currentRoom = %d\n", room, currentRoom); fflush(stdout);
+ debugCN(kDebugScript, "room = %d; currentRoom = %d\n", room, currentRoom);
if (room != currentRoom) {
- printf("Kernel::handleTrigger() Trigger from another room\n");
+ debugCN(kDebugScript, "Kernel::handleTrigger() Trigger from another room\n");
return false;
}
@@ -123,8 +123,7 @@ bool Kernel::handleTrigger(int32 triggerNum) {
break;
case KT_DAEMON:
- printf("KT_DAEMON\n");
- fflush(stdout);
+ debugCN(kDebugScript, "KT_DAEMON\n");
triggerMode = KT_DAEMON;
daemonTriggerAvailable = false;
roomDaemon();
@@ -140,7 +139,7 @@ bool Kernel::handleTrigger(int32 triggerNum) {
break;
default:
- printf("Kernel::handleTrigger() Unknown trigger mode %d\n", mode);
+ debugCN(kDebugScript, "Kernel::handleTrigger() Unknown trigger mode %d\n", mode);
}
@@ -181,7 +180,7 @@ void Kernel::globalDaemon() {
if (_globalDaemonFn)
_vm->_script->runFunction(_globalDaemonFn);
else {
- printf("Kernel::globalDaemon() _globalDaemonFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::globalDaemon() _globalDaemonFn is NULL\n");
}
}
@@ -189,7 +188,7 @@ void Kernel::globalParser() {
if (_globalParserFn)
_vm->_script->runFunction(_globalParserFn);
else {
- printf("Kernel::globalParser() _globalParserFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::globalParser() _globalParserFn is NULL\n");
}
}
@@ -197,7 +196,7 @@ void Kernel::sectionInit() {
if (_sectionInitFn)
_vm->_script->runFunction(_sectionInitFn);
else {
- printf("Kernel::sectionInit() _sectionInitFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::sectionInit() _sectionInitFn is NULL\n");
}
}
@@ -205,7 +204,7 @@ void Kernel::sectionDaemon() {
if (_sectionDaemonFn)
_vm->_script->runFunction(_sectionDaemonFn);
else {
- printf("Kernel::sectionDaemon() _sectionDaemonFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::sectionDaemon() _sectionDaemonFn is NULL\n");
}
}
@@ -213,7 +212,7 @@ void Kernel::sectionParser() {
if (_sectionParserFn)
_vm->_script->runFunction(_sectionParserFn);
else {
- printf("Kernel::sectionParser() _sectionParserFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::sectionParser() _sectionParserFn is NULL\n");
}
}
@@ -221,7 +220,7 @@ void Kernel::roomInit() {
if (_roomInitFn)
_vm->_script->runFunction(_roomInitFn);
else {
- printf("Kernel::roomInit() _roomInitFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::roomInit() _roomInitFn is NULL\n");
}
}
@@ -229,7 +228,7 @@ void Kernel::roomDaemon() {
if (_roomDaemonFn)
_vm->_script->runFunction(_roomDaemonFn);
else {
- printf("Kernel::roomDaemon() _roomDaemonFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::roomDaemon() _roomDaemonFn is NULL\n");
}
}
@@ -237,7 +236,7 @@ void Kernel::roomPreParser() {
if (_roomPreParserFn)
_vm->_script->runFunction(_roomPreParserFn);
else {
- printf("Kernel::roomPreParser() _roomPreParserFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::roomPreParser() _roomPreParserFn is NULL\n");
}
}
@@ -245,7 +244,7 @@ void Kernel::roomParser() {
if (_roomParserFn)
_vm->_script->runFunction(_roomParserFn);
else {
- printf("Kernel::roomParser() _roomParserFn is NULL\n");
+ debugCN(kDebugScript, "Kernel::roomParser() _roomParserFn is NULL\n");
}
}
@@ -351,7 +350,7 @@ void MadsGlobals::loadMadsMessagesInfo() {
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
int16 count = messageS->readUint16LE();
- //printf("%i messages\n", count);
+ //debugCN(kDebugScript, "%i messages\n", count);
for (int i = 0; i < count; i++) {
MessageItem curMessage;
@@ -365,7 +364,7 @@ void MadsGlobals::loadMadsMessagesInfo() {
if (i == count - 1)
curMessage.compSize = messageS->size() - curMessage.offset;
- //printf("id: %i, offset: %i, uncomp size: %i\n", curMessage->id, curMessage->offset, curMessage->uncompSize);
+ //debugCN(kDebugScript, "id: %i, offset: %i, uncomp size: %i\n", curMessage->id, curMessage->offset, curMessage->uncompSize);
_madsMessages.push_back(curMessage);
}
diff --git a/engines/m4/globals.h b/engines/m4/globals.h
index 67dcfc2b94..117769ee8e 100644
--- a/engines/m4/globals.h
+++ b/engines/m4/globals.h
@@ -229,15 +229,18 @@ struct MadsConfigData {
#define SET_GLOBAL(x,y) _madsVm->globals()->_globals[x] = y
#define SET_GLOBAL32(x,y) { _madsVm->globals()->_globals[x] = (y) & 0xffff; _madsVm->globals()->_globals[(x) + 1] = (y) >> 16; }
+typedef int (*IntFunctionPtr)();
+
union DataMapEntry {
bool *boolValue;
uint16 *uint16Value;
int *intValue;
+ IntFunctionPtr fnPtr;
};
typedef Common::HashMap<uint16, uint16> DataMapHash;
-enum DataMapType {BOOL, UINT16, INT};
+enum DataMapType {BOOL, UINT16, INT, INT_FN};
class DataMapWrapper {
friend class DataMap;
@@ -249,16 +252,18 @@ public:
DataMapWrapper(uint16 *v) { _value.uint16Value = v; _type = UINT16; }
DataMapWrapper(int16 *v) { _value.uint16Value = (uint16 *)v; _type = UINT16; }
DataMapWrapper(int *v) { _value.intValue = v; _type = INT; }
+ DataMapWrapper(IntFunctionPtr v) { _value.fnPtr = v; _type = INT_FN; }
uint16 getIntValue() {
if (_type == BOOL) return *_value.boolValue ? 0xffff : 0;
else if (_type == UINT16) return *_value.uint16Value;
- else return *_value.intValue;
+ else if (_type == INT) return *_value.intValue;
+ else return _value.fnPtr();
}
void setIntValue(uint16 v) {
if (_type == BOOL) *_value.boolValue = v != 0;
else if (_type == UINT16) *_value.uint16Value = v;
- else *_value.intValue = v;
+ else if (_type == INT) *_value.intValue = v;
}
};
diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp
index 423dda5e7e..eb2fc9e4b5 100644
--- a/engines/m4/graphics.cpp
+++ b/engines/m4/graphics.cpp
@@ -213,8 +213,8 @@ void M4Surface::drawSprite(int x, int y, SpriteInfo &info, const Common::Rect &c
int scaledHeight = scaleValue(info.height, info.scaleY, errY);
/*
- printf("M4Surface::drawSprite() info.width = %d; info.scaleX = %d; info.height = %d; info.scaleY = %d; scaledWidth = %d; scaledHeight = %d\n",
- info.width, info.scaleX, info.height, info.scaleY, scaledWidth, scaledHeight); fflush(stdout);
+ debugCN(kDebugGraphics, "M4Surface::drawSprite() info.width = %d; info.scaleX = %d; info.height = %d; info.scaleY = %d; scaledWidth = %d; scaledHeight = %d\n",
+ info.width, info.scaleX, info.height, info.scaleY, scaledWidth, scaledHeight);
*/
int clipX = 0, clipY = 0;
@@ -233,7 +233,7 @@ void M4Surface::drawSprite(int x, int y, SpriteInfo &info, const Common::Rect &c
scaledHeight = y + scaledHeight;
}
- //printf("M4Surface::drawSprite() width = %d; height = %d; scaledWidth = %d; scaledHeight = %d\n", info.width, info.height, scaledWidth, scaledHeight); fflush(stdout);
+ //debugCN(kDebugGraphics, "M4Surface::drawSprite() width = %d; height = %d; scaledWidth = %d; scaledHeight = %d\n", info.width, info.height, scaledWidth, scaledHeight);
// Check if sprite is inside the screen. If it's not, there's no need to draw it
if (scaledWidth + x <= 0 || scaledHeight + y <= 0) // check left and top (in case x,y are negative)
@@ -703,7 +703,7 @@ void M4Surface::madsLoadBackground(int roomNumber, RGBList **palData) {
else
compressedTileDataSize = tileDataUncomp->readUint32LE() - tileOfs;
- //printf("Tile: %i, compressed size: %i\n", i, compressedTileDataSize);
+ //debugCN(kDebugGraphics, "Tile: %i, compressed size: %i\n", i, compressedTileDataSize);
newTile->clear();
@@ -740,7 +740,7 @@ void M4Surface::madsLoadBackground(int roomNumber, RGBList **palData) {
void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData) {
MadsPack packData(source);
- Common::MemoryReadStream *sourceUnc = packData.getItemStream(0);
+ Common::SeekableReadStream *sourceUnc = packData.getItemStream(0);
int sceneWidth = sourceUnc->readUint16LE();
int sceneHeight = sourceUnc->readUint16LE();
@@ -787,7 +787,7 @@ void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) {
uint8 blackIndex = 0;
// Debug
- //printf("loadBackground(): %dx%d picture (%d bytes) - %dx%d tiles of size %dx%d\n",
+ //debugCN(kDebugGraphics, "loadBackground(): %dx%d picture (%d bytes) - %dx%d tiles of size %dx%d\n",
// widthVal, heightVal, size, tilesX, tilesY, tileWidth, tileHeight);
// BGR data, which is converted to RGB8
@@ -807,7 +807,7 @@ void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) {
// note that the height of the scene in game scenes is smaller than 480, as the bottom part of the
// screen is the inventory
assert(width() == (int)widthVal);
- //printf("width(): %d, widthVal: %d, height(): %d, heightVal: %d\n", width(), widthVal, height(), heightVal);
+ //debugCN(kDebugGraphics, "width(): %d, widthVal: %d, height(): %d, heightVal: %d\n", width(), widthVal, height(), heightVal);
tileBuffer->create(tileWidth, tileHeight, 1);
diff --git a/engines/m4/hotspot.cpp b/engines/m4/hotspot.cpp
index 27180c5eb8..500464dc09 100644
--- a/engines/m4/hotspot.cpp
+++ b/engines/m4/hotspot.cpp
@@ -227,7 +227,7 @@ void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int ho
// This looks to be some sort of bitmask. Perhaps it signifies
// the valid verbs for this hotspot
index = hotspotStream->readUint16LE(); // unknown
- //printf("%i ", index);
+ //debugC(kDebugCore, "%i ", index);
}
if (_vm->isM4())
diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp
index a999a6bd5a..73dba87e99 100644
--- a/engines/m4/m4.cpp
+++ b/engines/m4/m4.cpp
@@ -121,6 +121,8 @@ MadsM4Engine::MadsM4Engine(OSystem *syst, const M4GameDescription *gameDesc) :
DebugMan.addDebugChannel(kDebugScript, "script", "Script debug level");
DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics debug level");
DebugMan.addDebugChannel(kDebugConversations, "conversations", "Conversations debugging");
+ DebugMan.addDebugChannel(kDebugSound, "sound", "Sounds debug level");
+ DebugMan.addDebugChannel(kDebugCore, "core", "Core debug level");
_resourceManager = NULL;
_globals = NULL;
@@ -266,33 +268,39 @@ void MadsM4Engine::loadMenu(MenuType menuType, bool loadSaveFromHotkey, bool cal
_viewManager->moveToFront(view);
}
+#define DUMP_BUFFER_SIZE 1024
+
void MadsM4Engine::dumpFile(const char* filename, bool uncompress) {
+ Common::DumpFile f;
+ byte buffer[DUMP_BUFFER_SIZE];
Common::SeekableReadStream *fileS = res()->get(filename);
- byte buffer[256];
- FILE *destFile = fopen(filename, "wb");
+
+ if (!f.open(filename))
+ error("Could not open '%s' for writing", filename);
+
int bytesRead = 0;
- printf("Dumping %s, size: %i\n", filename, fileS->size());
+ warning("Dumping %s, size: %i\n", filename, fileS->size());
if (!uncompress) {
while (!fileS->eos()) {
- bytesRead = fileS->read(buffer, 256);
- fwrite(buffer, bytesRead, 1, destFile);
+ bytesRead = fileS->read(buffer, DUMP_BUFFER_SIZE);
+ f.write(buffer, bytesRead);
}
} else {
MadsPack packData(fileS);
- Common::MemoryReadStream *sourceUnc;
+ Common::SeekableReadStream *sourceUnc;
for (int i = 0; i < packData.getCount(); i++) {
sourceUnc = packData.getItemStream(i);
- printf("Dumping compressed chunk %i of %i, size is %i\n", i + 1, packData.getCount(), sourceUnc->size());
+ debugCN(kDebugCore, "Dumping compressed chunk %i of %i, size is %i\n", i + 1, packData.getCount(), sourceUnc->size());
while (!sourceUnc->eos()) {
- bytesRead = sourceUnc->read(buffer, 256);
- fwrite(buffer, bytesRead, 1, destFile);
+ bytesRead = sourceUnc->read(buffer, DUMP_BUFFER_SIZE);
+ f.write(buffer, bytesRead);
}
delete sourceUnc;
}
}
- fclose(destFile);
+ f.close();
res()->toss(filename);
res()->purge();
}
@@ -338,8 +346,7 @@ Common::Error M4Engine::run() {
for (int i = 1; i < 58; i++) {
_vm->_kernel->trigger = i;
_script->runFunction(func);
- printf("=================================\n");
- fflush(stdout);
+ debugCN(kDebugCore, "=================================\n");
}
#endif
@@ -535,7 +542,7 @@ Common::Error MadsEngine::run() {
// Test code to dump all messages to the console
//for (int i = 0; i < _globals->getMessagesSize(); i++)
- //printf("%s\n----------\n", _globals->loadMessage(i));
+ //debugCN(kDebugCore, "%s\n----------\n", _globals->loadMessage(i));
if (getGameType() == GType_RexNebular) {
MadsGameLogic::initialiseGlobals();
diff --git a/engines/m4/m4.h b/engines/m4/m4.h
index b68f7248af..8069c960f1 100644
--- a/engines/m4/m4.h
+++ b/engines/m4/m4.h
@@ -70,7 +70,7 @@
* functionality has been implemented. No further work has been done on this for some time, so progress
* on this part of the engine can be considered frozen.
*
- * Games this engine will support:
+ * Games using this engine:
* MADS Games: Dragonsphere, Return of the Phantom, Rex Nebular and the Cosmic Gender Bender
* M4 Games: Orion Burger, The Riddle of Master Lu
*/
@@ -116,7 +116,9 @@ enum {
enum {
kDebugScript = 1 << 0,
kDebugConversations = 1 << 1,
- kDebugGraphics = 1 << 2
+ kDebugGraphics = 1 << 2,
+ kDebugSound = 1 << 3,
+ kDebugCore = 1 << 4
};
#define MESSAGE_BASIC 1
@@ -161,7 +163,6 @@ public:
const char *getGameFile(int fileType);
Common::EventManager *eventMan() { return _eventMan; }
- OSystem *system() { return _system; }
const M4GameDescription *_gameDescription;
diff --git a/engines/m4/m4_scene.cpp b/engines/m4/m4_scene.cpp
index 475fdba653..738f414add 100644
--- a/engines/m4/m4_scene.cpp
+++ b/engines/m4/m4_scene.cpp
@@ -61,7 +61,7 @@ void M4Scene::loadSceneSprites(int sceneNumber) {
_sceneSprites = new SpriteAsset(_vm, sceneS, sceneS->size(), filename);
_vm->res()->toss(filename);
- printf("Scene has %d sprites, each one having %d colors\n", _sceneSprites->getCount(), _sceneSprites->getColorCount());
+ debugCN(kDebugGraphics, "Scene has %d sprites, each one having %d colors\n", _sceneSprites->getCount(), _sceneSprites->getColorCount());
}
void M4Scene::loadSceneResources(int sceneNumber) {
@@ -136,7 +136,7 @@ void M4Scene::loadSceneSpriteCodes(int sceneNumber) {
// RGB8* spritePalette = _sceneSpriteCodes->getPalette();
//_vm->_palette->setPalette(spritePalette, 0, colorCount);
- printf("Scene has %d sprite codes, each one having %d colors\n", _sceneSpriteCodes->getCount(), colorCount);
+ debugCN(kDebugGraphics, "Scene has %d sprite codes, each one having %d colors\n", _sceneSpriteCodes->getCount(), colorCount);
// Note that toss() deletes the MemoryReadStream
_vm->res()->toss(filename);
@@ -252,7 +252,7 @@ void M4Scene::leftClick(int x, int y) {
*/
// Player said.... (for scene scripts)
- printf("Player said: %s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
+ debugCN(kDebugGraphics, "Player said: %s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
// FIXME: This should be moved somewhere else, and is incomplete
if (_m4Vm->scene()->getInterface()->_inventory.getSelectedIndex() == -1) {
@@ -268,7 +268,7 @@ void M4Scene::leftClick(int x, int y) {
strcpy(_vm->_player->object, "");
_vm->_player->commandReady = true;
- printf("## Player said: %s %s\n", _vm->_player->verb, _vm->_player->noun);
+ debugCN(kDebugGraphics, "## Player said: %s %s\n", _vm->_player->verb, _vm->_player->noun);
}
}
diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp
index ca53bdca75..1d15cbbaf7 100644
--- a/engines/m4/mads_anim.cpp
+++ b/engines/m4/mads_anim.cpp
@@ -721,21 +721,21 @@ void AnimviewView::processCommand() {
case 'O':
param = param + 2;
- //printf("O:%i ", atoi(param));
+ //warning(kDebugGraphics, "O:%i ", atoi(param));
_transition = atoi(param);
break;
case 'R':
param = param + 2;
- //printf("R:%s ", param);
+ //warning(kDebugGraphics, "R:%s ", param);
break;
case 'W':
- //printf("W ");
+ //warning(kDebugGraphics, "W ");
break;
case 'X':
- //printf("X ");
+ //warning(kDebugGraphics, "X ");
break;
default:
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index cebe2215ca..878e86c573 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -182,7 +182,9 @@ void MadsSceneLogic::initialiseDataMap() {
MAP_DATA(&_madsVm->_player._playerPos.y);
MAP_DATA(&_madsVm->_player._direction);
MAP_DATA(&_madsVm->_player._visible);
- MAP_DATA(&_madsVm->scene()->_animActive);
+ MAP_DATA(&getActiveAnimationBool);
+ MAP_DATA(&getAnimationCurrentFrame);
+
}
DataMap &MadsSceneLogic::dataMap() {
@@ -658,12 +660,12 @@ void MadsSceneLogic::execute(uint32 subOffset) {
case OP_NOT: // logical nots top item on stack
param = stack.pop().get();
- stack.push(ScriptVar((uint32)!param & 0xffff));
+ stack.push(ScriptVar((uint32)(!param) & 0xffff));
break;
case OP_COMP: // complements top item on stack
param = stack.pop().get();
- stack.push(ScriptVar(~param & 0xffff));
+ stack.push(ScriptVar((~param) & 0xffff));
break;
case OP_NEG: // negates top item on stack
diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp
index 7e2e6f500b..91db7d343a 100644
--- a/engines/m4/mads_menus.cpp
+++ b/engines/m4/mads_menus.cpp
@@ -546,7 +546,7 @@ int DragonMainMenuView::getHighlightedItem(int x, int y) {
M4Sprite *spr = _menuItem->getFrame(0);
if ((x >= pt.x - 25) && (y >= pt.y - spr->height()) && (x < (pt.x - 25 + spr->width())) && (y < (pt.y))) {
- printf("x = %d, y = %d, index = %d\n", x, y, index);
+ debugCN(kDebugGraphics, "x = %d, y = %d, index = %d\n", x, y, index);
return index;
}
}
diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp
index 641ee756e3..e88a21eb4e 100644
--- a/engines/m4/mads_scene.cpp
+++ b/engines/m4/mads_scene.cpp
@@ -62,7 +62,6 @@ void SceneNode::load(Common::SeekableReadStream *stream) {
MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) {
_vm = vm;
_activeAnimation = NULL;
- _animActive = false;
MadsView::_bgSurface = Scene::_backgroundSurface;
MadsView::_depthSurface = Scene::_walkSurface;
@@ -217,7 +216,6 @@ void MadsScene::leaveScene() {
if (_activeAnimation) {
delete _activeAnimation;
_activeAnimation = NULL;
- _animActive = false;
}
Scene::leaveScene();
@@ -386,7 +384,6 @@ void MadsScene::updateState() {
if (((MadsAnimation *) _activeAnimation)->freeFlag() || freeFlag) {
delete _activeAnimation;
_activeAnimation = NULL;
- _animActive = false;
}
}
@@ -458,7 +455,6 @@ void MadsScene::freeAnimation() {
delete _activeAnimation;
_activeAnimation = NULL;
- _animActive = false;
}
@@ -578,7 +574,6 @@ void MadsScene::loadAnimation(const Common::String &animName, int abortTimers) {
MadsAnimation *anim = new MadsAnimation(_vm, this);
anim->load(animName.c_str(), abortTimers);
_activeAnimation = anim;
- _animActive = true;
}
bool MadsScene::getDepthHighBit(const Common::Point &pt) {
@@ -1244,4 +1239,16 @@ void MadsInterfaceView::leaveScene() {
_madsVm->_viewManager->deleteView(view);
}
+//--------------------------------------------------------------------------
+
+int getActiveAnimationBool() {
+ return (_madsVm->scene()->activeAnimation()) ? 1 : 0;
+}
+
+int getAnimationCurrentFrame() {
+ Animation *anim = _madsVm->scene()->activeAnimation();
+ return anim ? anim->getCurrentFrame() : 0;
+}
+
+
} // End of namespace M4
diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h
index a029c63a4b..743719d954 100644
--- a/engines/m4/mads_scene.h
+++ b/engines/m4/mads_scene.h
@@ -108,7 +108,6 @@ public:
Common::Point _destPos;
int _destFacing;
Common::Point _customDest;
- bool _animActive;
public:
MadsScene(MadsEngine *vm);
virtual ~MadsScene();
@@ -192,6 +191,9 @@ public:
bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents);
};
+extern int getActiveAnimationBool();
+extern int getAnimationCurrentFrame();
+
} // End of namespace M4
#endif
diff --git a/engines/m4/midi.cpp b/engines/m4/midi.cpp
index 2c767fdf5a..f130ddc3b5 100644
--- a/engines/m4/midi.cpp
+++ b/engines/m4/midi.cpp
@@ -28,7 +28,7 @@
#include "m4/m4.h"
#include "m4/midi.h"
-#include "common/stream.h"
+#include "common/memstream.h"
namespace M4 {
diff --git a/engines/m4/rails.cpp b/engines/m4/rails.cpp
index 11b9bcdbb9..ff18d645e7 100644
--- a/engines/m4/rails.cpp
+++ b/engines/m4/rails.cpp
@@ -233,7 +233,7 @@ void Rails::createEdge(int32 node1, int32 node2) {
valid = isLineWalkable(_nodes[node1]->x, _nodes[node1]->y,
_nodes[node2]->x, _nodes[node2]->y);
- printf("test code says: %d\n", valid);
+ debugCN(kDebugCore, "test code says: %d\n", valid);
// Check if the line passes through a forbidden rectangle
if (valid) {
@@ -255,7 +255,7 @@ void Rails::createEdge(int32 node1, int32 node2) {
_edges.insert_at(index, (int16*)(distance >> 16));
}
- printf("node1 = %d, node2 = %d, valid = %d\n", node1, node2, valid);
+ debugCN(kDebugCore, "node1 = %d, node2 = %d, valid = %d\n", node1, node2, valid);
}
diff --git a/engines/m4/resource.cpp b/engines/m4/resource.cpp
index eee1214e7f..70abc47960 100644
--- a/engines/m4/resource.cpp
+++ b/engines/m4/resource.cpp
@@ -27,6 +27,8 @@
#include "m4/resource.h"
#include "m4/events.h"
+#include "common/substream.h"
+
namespace M4 {
FileSystem::FileSystem(const char *hashFilename) {
@@ -43,12 +45,12 @@ FileSystem::FileSystem(const char *hashFilename) {
hashFile.open(hashFilename);
if (!hashFile.isOpen()) {
- printf("FileSystem::FileSystem: error opening hash %s\n", hashFilename);
+ debugCN(kDebugCore, "FileSystem::FileSystem: error opening hash %s\n", hashFilename);
}
hashSize = hashFile.readUint32LE();
- //printf("FileSystem::FileSystem: hashSize = %d\n", hashSize);
+ //debugCN(kDebugCore, "FileSystem::FileSystem: hashSize = %d\n", hashSize);
/* load file records and add them to the hash list */
for (uint i = 0; i < hashSize; i++) {
@@ -63,12 +65,12 @@ FileSystem::FileSystem(const char *hashFilename) {
if (entry.filename[0]) {
/*
- printf(" filename: %s\n", entry.filename);
- printf(" hagfile: %d\n", entry.hagfile);
- printf(" disks: %d\n", entry.disks);
- printf(" offset: %08X\n", entry.offset);
- printf(" size: %d\n", entry.size);
- printf(" next: %08X\n", entry.next);
+ debugCN(kDebugCore, " filename: %s\n", entry.filename);
+ debugCN(kDebugCore, " hagfile: %d\n", entry.hagfile);
+ debugCN(kDebugCore, " disks: %d\n", entry.disks);
+ debugCN(kDebugCore, " offset: %08X\n", entry.offset);
+ debugCN(kDebugCore, " size: %d\n", entry.size);
+ debugCN(kDebugCore, " next: %08X\n", entry.next);
*/
_fileEntries[entry.filename] = entry;
}
@@ -90,7 +92,7 @@ FileSystem::FileSystem(const char *hashFilename) {
_hagEntries[entry.fileIndex].hagFile->open(_hagEntries[entry.fileIndex].filename);
if (!_hagEntries[entry.fileIndex].hagFile->isOpen()) {
- printf("FileSystem::FileSystem: error opening hag %s\n", _hagEntries[entry.fileIndex].filename);
+ debugCN(kDebugCore, "FileSystem::FileSystem: error opening hag %s\n", _hagEntries[entry.fileIndex].filename);
}
}
@@ -113,7 +115,7 @@ Common::SeekableReadStream *FileSystem::loadFile(const char *resourceName, bool
Common::SeekableReadStream *result = NULL;
if (hfe) {
- //printf("FileSystem::loadFile() success opening %s\n", filename);
+ //debugCN(kDebugCore, "FileSystem::loadFile() success opening %s\n", filename);
HashHagEntry *hagEntry = &_hagEntries[hfe->hagfile];
if (preloadFlag) {
@@ -128,7 +130,7 @@ Common::SeekableReadStream *FileSystem::loadFile(const char *resourceName, bool
hfe->offset + hfe->size);
} else {
- printf("FileSystem::loadFile() error opening %s\n", resourceName);
+ debugCN(kDebugCore, "FileSystem::loadFile() error opening %s\n", resourceName);
}
return result;
@@ -207,7 +209,7 @@ void ResourceManager::toss(const char *resourceName) {
if (!strcmp(r->name, resourceName)) {
r->flags |= kResourcePurge;
- //printf("M4ResourceManager::toss: mark resource %s to be purged\n", resourceName);
+ //debugCN(kDebugCore, "M4ResourceManager::toss: mark resource %s to be purged\n", resourceName);
}
}
}
@@ -510,7 +512,7 @@ M4ResourceManager::~M4ResourceManager() {
}
Common::SeekableReadStream *M4ResourceManager::loadResource(const char *resourceName, bool preloadFlag) {
- //printf("M4ResourceManager::loadResource() loading resource %s\n", resourceName);
+ //debugCN(kDebugCore, "M4ResourceManager::loadResource() loading resource %s\n", resourceName);
Common::SeekableReadStream* result = NULL;
if (_hfs) {
// actually load the resource
diff --git a/engines/m4/script.cpp b/engines/m4/script.cpp
index 412707d95e..42d55c6dc7 100644
--- a/engines/m4/script.cpp
+++ b/engines/m4/script.cpp
@@ -121,7 +121,7 @@ SeriesStreamBreakList::~SeriesStreamBreakList() {
void SeriesStreamBreakList::load(Common::File *fd) {
uint32 count = fd->readUint32LE();
- printf("SeriesStreamBreakList::load() count = %d\n", count);
+ debugCN(kDebugScript, "SeriesStreamBreakList::load() count = %d\n", count);
for (uint32 i = 0; i < count; i++) {
SeriesStreamBreakItem *item = new SeriesStreamBreakItem();
item->frameNum = fd->readUint32LE();
@@ -135,7 +135,7 @@ void SeriesStreamBreakList::load(Common::File *fd) {
item->value = fd->readUint32LE();
_items.push_back(item);
- printf("%02d: frameNum = %d; digiName = %s; digiChannel = %d; digiVolume = %d; trigger = %d; flags = %d; variable = %d; value = %d\n",
+ debugCN(kDebugScript, "%02d: frameNum = %d; digiName = %s; digiChannel = %d; digiVolume = %d; trigger = %d; flags = %d; variable = %d; value = %d\n",
i, item->frameNum, item->digiName, item->digiChannel, item->digiVolume, item->trigger, item->flags, item->variable.value, item->value);
}
@@ -146,7 +146,7 @@ SaidArray::~SaidArray() {
void SaidArray::load(Common::File *fd) {
uint32 count = fd->readUint32LE();
- printf("SaidArray::load() count = %d\n", count);
+ debugCN(kDebugScript, "SaidArray::load() count = %d\n", count);
for (uint32 i = 0; i < count; i++) {
SaidArrayItem *item = new SaidArrayItem();
item->itemName = _inter->loadGlobalString(fd);
@@ -155,7 +155,7 @@ void SaidArray::load(Common::File *fd) {
item->digiNameGear = _inter->loadGlobalString(fd);
_items.push_back(item);
- printf("itemName = %s; digiNameLook = %s; digiNameTake = %s; digiNameGear = %s\n",
+ debugCN(kDebugScript, "itemName = %s; digiNameLook = %s; digiNameTake = %s; digiNameGear = %s\n",
item->itemName, item->digiNameLook, item->digiNameTake, item->digiNameGear);
}
@@ -166,7 +166,7 @@ ParserArray::~ParserArray() {
void ParserArray::load(Common::File *fd) {
uint32 count = fd->readUint32LE();
- printf("ParserArray::load() count = %d\n", count);
+ debugCN(kDebugScript, "ParserArray::load() count = %d\n", count);
for (uint32 i = 0; i < count; i++) {
ParserArrayItem *item = new ParserArrayItem();
item->w0 = _inter->loadGlobalString(fd);
@@ -180,7 +180,7 @@ void ParserArray::load(Common::File *fd) {
item->value = fd->readUint32LE();
_items.push_back(item);
- printf("w0 = %s; w1 = %s; trigger = %d; testVariable = %d; testValue = %d; variable = %d; value = %d\n",
+ debugCN(kDebugScript, "w0 = %s; w1 = %s; trigger = %d; testVariable = %d; testValue = %d; variable = %d; value = %d\n",
item->w0, item->w1, item->trigger, item->testVariable.value, item->testValue, item->variable.value, item->value);
}
@@ -194,9 +194,9 @@ ScriptFunction::~ScriptFunction() {
}
void ScriptFunction::load(Common::File *fd) {
- printf("ScriptFunction::load()\n");
+ debugCN(kDebugScript, "ScriptFunction::load()\n");
uint32 size = fd->readUint32LE();
- printf("ScriptFunction::load() size = %d\n", size);
+ debugCN(kDebugScript, "ScriptFunction::load() size = %d\n", size);
_code = fd->readStream(size);
}
@@ -243,16 +243,16 @@ void ScriptInterpreter::open(const char *filename) {
}
int functionCount = _scriptFile->readUint32LE();
- printf("functionCount = %d\n", functionCount);
+ debugCN(kDebugScript, "functionCount = %d\n", functionCount);
for (int i = 0; i < functionCount; i++) {
uint32 offset = _scriptFile->readUint32LE();
- printf("func(%d) offset = %08X\n", i, offset);
+ debugCN(kDebugScript, "func(%d) offset = %08X\n", i, offset);
uint32 len = _scriptFile->readUint32LE();
if (len > 0) {
char *funcName = new char[len + 1];
_scriptFile->read(funcName, len);
funcName[len] = '\0';
- printf("func(%d) name = %s\n", i, funcName);
+ debugCN(kDebugScript, "func(%d) name = %s\n", i, funcName);
_functionNames[Common::String(funcName)] = _functions.size();
// DEBUG
_scriptFunctionNames.push_back(Common::String(funcName));
@@ -262,16 +262,16 @@ void ScriptInterpreter::open(const char *filename) {
}
int dataCount = _scriptFile->readUint32LE();
- printf("dataCount = %d\n", dataCount);
+ debugCN(kDebugScript, "dataCount = %d\n", dataCount);
for (int i = 0; i < dataCount; i++) {
uint32 offset = _scriptFile->readUint32LE();
ScriptDataType type = (ScriptDataType)_scriptFile->readUint32LE();
- printf("data(%d) offset = %08X; type = %d\n", i, offset, type);
+ debugCN(kDebugScript, "data(%d) offset = %08X; type = %d\n", i, offset, type);
_data.push_back(new ScriptDataEntry(offset, type));
}
_globalVarCount = _scriptFile->readUint32LE();
- printf("_globalVarCount = %d\n", _globalVarCount);
+ debugCN(kDebugScript, "_globalVarCount = %d\n", _globalVarCount);
uint32 stringOfs = _scriptFile->readUint32LE();
_scriptFile->seek(stringOfs);
@@ -324,11 +324,11 @@ ScriptFunction *ScriptInterpreter::loadFunction(uint32 index) {
ScriptFunction *ScriptInterpreter::loadFunction(const Common::String &name) {
FunctionNameMap::iterator iter = _functionNames.find(name);
if (iter == _functionNames.end()) {
- printf("ScriptInterpreter::loadFunction() Function '%s' not found!\n", name.c_str());
+ debugCN(kDebugScript, "ScriptInterpreter::loadFunction() Function '%s' not found!\n", name.c_str());
return NULL;
}
uint32 funcIndex = (*iter)._value;
- printf("ScriptInterpreter::loadFunction() index('%s') = %d\n", name.c_str(), funcIndex);
+ debugCN(kDebugScript, "ScriptInterpreter::loadFunction() index('%s') = %d\n", name.c_str(), funcIndex);
return loadFunction(funcIndex);
}
@@ -354,7 +354,6 @@ int ScriptInterpreter::runFunction(ScriptFunction *scriptFunction) {
while (!done) {
byte opcode = _runningFunction->readByte();
done = !execOpcode(opcode);
- fflush(stdout);
}
_localStackPtr = oldLocalStackPtr;
@@ -376,24 +375,24 @@ void ScriptInterpreter::pop(ScriptValue &value) {
}
void ScriptInterpreter::dumpStack() {
- printf("ScriptInterpreter::dumpStack()\n");
+ debugCN(kDebugScript, "ScriptInterpreter::dumpStack()\n");
for (int i = 0; i < _stackPtr; i++) {
- printf("%03d. type = %02d; value = %d\n", i, _stack[i].type, _stack[i].value);
+ debugCN(kDebugScript, "%03d. type = %02d; value = %d\n", i, _stack[i].type, _stack[i].value);
}
}
void ScriptInterpreter::dumpRegisters() {
- printf("ScriptInterpreter::dumpRegisters()\n");
+ debugCN(kDebugScript, "ScriptInterpreter::dumpRegisters()\n");
for (int i = 0; i < ARRAYSIZE(_registers); i++) {
- printf("%03d. type = %02d; value = %d\n", i, _registers[i].type, _registers[i].value);
+ debugCN(kDebugScript, "%03d. type = %02d; value = %d\n", i, _registers[i].type, _registers[i].value);
}
}
void ScriptInterpreter::dumpGlobalVars() {
- printf("ScriptInterpreter::dumpGlobalVars()\n");
+ debugCN(kDebugScript, "ScriptInterpreter::dumpGlobalVars()\n");
for (int i = 0; i < ARRAYSIZE(_globalVars); i++) {
if (_globalVars[i].type != -1)
- printf("%03d. type = %02d; value = %d\n", i, _globalVars[i].type, _globalVars[i].value);
+ debugCN(kDebugScript, "%03d. type = %02d; value = %d\n", i, _globalVars[i].type, _globalVars[i].value);
}
}
@@ -405,7 +404,7 @@ int ScriptInterpreter::toInteger(const ScriptValue &value) {
return value.value;
default:
- printf("ScriptInterpreter::toInteger() Invalid type %d!\n", value.type);
+ debugCN(kDebugScript, "ScriptInterpreter::toInteger() Invalid type %d!\n", value.type);
return 0;
}
@@ -423,7 +422,7 @@ const char *ScriptInterpreter::toString(const ScriptValue &value) {
return _constStrings[value.value];
default:
- printf("ScriptInterpreter::toString() Invalid type %d!\n", value.type);
+ debugCN(kDebugScript, "ScriptInterpreter::toString() Invalid type %d!\n", value.type);
return NULL;
}
@@ -462,7 +461,7 @@ void ScriptInterpreter::loadValue(ScriptValue &value) {
break;
default:
- printf("ScriptInterpreter::loadValue() Invalid value type %d!\n", value.type);
+ debugCN(kDebugScript, "ScriptInterpreter::loadValue() Invalid value type %d!\n", value.type);
}
@@ -471,7 +470,7 @@ void ScriptInterpreter::loadValue(ScriptValue &value) {
void ScriptInterpreter::copyValue(ScriptValue &destValue, ScriptValue &sourceValue) {
if (sourceValue.type == -1) {
- printf("ScriptInterpreter::copyValue() Trying to read uninitialized value!\n");
+ debugCN(kDebugScript, "ScriptInterpreter::copyValue() Trying to read uninitialized value!\n");
}
switch (destValue.type) {
@@ -489,7 +488,7 @@ void ScriptInterpreter::copyValue(ScriptValue &destValue, ScriptValue &sourceVal
if (sourceValue.type == kInteger) {
_logicGlobals[destValue.value] = sourceValue.value;
} else {
- printf("ScriptInterpreter::copyValue() Invalid source value type %d!\n", sourceValue.type);
+ debugCN(kDebugScript, "ScriptInterpreter::copyValue() Invalid source value type %d!\n", sourceValue.type);
}
break;
@@ -498,7 +497,7 @@ void ScriptInterpreter::copyValue(ScriptValue &destValue, ScriptValue &sourceVal
break;
default:
- printf("ScriptInterpreter::copyValue() Invalid dest value type %d!\n", destValue.type);
+ debugCN(kDebugScript, "ScriptInterpreter::copyValue() Invalid dest value type %d!\n", destValue.type);
}
@@ -533,7 +532,7 @@ void ScriptInterpreter::derefValue(ScriptValue &value) {
break;
default:
- printf("ScriptInterpreter::derefValue() Invalid value type %d!\n", value.type);
+ debugCN(kDebugScript, "ScriptInterpreter::derefValue() Invalid value type %d!\n", value.type);
}
@@ -541,21 +540,21 @@ void ScriptInterpreter::derefValue(ScriptValue &value) {
void ScriptInterpreter::callKernelFunction(uint32 index) {
- printf("ScriptInterpreter::callKernelFunction() index = %d\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::callKernelFunction() index = %d\n", index);
if (index > _kernelFunctionsMax) {
- printf("ScriptInterpreter::callKernelFunction() Invalid kernel functionindex (%d)\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::callKernelFunction() Invalid kernel functionindex (%d)\n", index);
return;
}
- printf("ScriptInterpreter::callKernelFunction() name = %s\n", _kernelFunctions[index].desc);
+ debugCN(kDebugScript, "ScriptInterpreter::callKernelFunction() name = %s\n", _kernelFunctions[index].desc);
int args = (this->*(_kernelFunctions[index].proc))();
// Now remove values from the stack if the function used any
if (args > 4)
_stackPtr -= args - 4;
- printf("-------------\n");
+ debugCN(kDebugScript, "-------------\n");
}
@@ -569,30 +568,29 @@ ScriptValue ScriptInterpreter::getArg(uint32 index) {
}
void ScriptInterpreter::dumpArgs(uint32 count) {
- printf("ScriptInterpreter::dumpArgs() ");
+ debugCN(kDebugScript, "ScriptInterpreter::dumpArgs() ");
for (uint32 i = 0; i < count; i++) {
ScriptValue argValue = getArg(i);
if (argValue.type == kConstString) {
- printf("'%s'", toString(argValue));
+ debugCN(kDebugScript, "'%s'", toString(argValue));
} else {
- printf("%d", argValue.value);
+ debugCN(kDebugScript, "%d", argValue.value);
}
if (i + 1 < count)
- printf(", ");
+ debugCN(kDebugScript, ", ");
}
- printf("\n");
+ debugCN(kDebugScript, "\n");
}
void ScriptInterpreter::callFunction(uint32 index) {
// NOTE: This is a temporary hack for script functions not yet in the m4.dat
if (index == 0xFFFFFFFF)
return;
- printf("ScriptInterpreter::callFunction() index = %d [%s]\n", index, _scriptFunctionNames[index].c_str());
- fflush(stdout);
+ debugCN(kDebugScript, "ScriptInterpreter::callFunction() index = %d [%s]\n", index, _scriptFunctionNames[index].c_str());
ScriptFunction *subFunction = loadFunction(index);
if (!subFunction) {
// This *should* never happen since the linker checks this
- printf("ScriptInterpreter::callFunction() Function %d could not be loaded!\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::callFunction() Function %d could not be loaded!\n", index);
return;
}
runFunction(subFunction);
@@ -600,7 +598,7 @@ void ScriptInterpreter::callFunction(uint32 index) {
bool ScriptInterpreter::execOpcode(byte opcode) {
- printf("opcode = %d (%s)\n", opcode, opcodeNames[opcode]);
+ debugCN(kDebugScript, "opcode = %d (%s)\n", opcode, opcodeNames[opcode]);
ScriptValue value1, value2, value3;
uint32 temp;
@@ -649,14 +647,14 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
case opJmp:
temp = _runningFunction->readUint32();
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
return true;
case opJl:
temp = _runningFunction->readUint32();
if (_cmpFlags < 0) {
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
}
return true;
@@ -664,7 +662,7 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
case opJle:
temp = _runningFunction->readUint32();
if (_cmpFlags <= 0) {
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
}
return true;
@@ -672,7 +670,7 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
case opJg:
temp = _runningFunction->readUint32();
if (_cmpFlags > 0) {
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
}
return true;
@@ -680,7 +678,7 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
case opJge:
temp = _runningFunction->readUint32();
if (_cmpFlags >= 0) {
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
}
return true;
@@ -688,7 +686,7 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
case opJz:
temp = _runningFunction->readUint32();
if (_cmpFlags == 0) {
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
}
return true;
@@ -696,17 +694,17 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
case opJnz:
temp = _runningFunction->readUint32();
if (_cmpFlags != 0) {
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
}
return true;
case opJmpByTable:
temp = _runningFunction->readUint32();
- printf("-> index = %d\n", _registers[0].value);
+ debugCN(kDebugScript, "-> index = %d\n", _registers[0].value);
_runningFunction->jumpRelative(_registers[0].value * 4);
temp = _runningFunction->readUint32();
- printf("-> ofs = %08X\n", temp);
+ debugCN(kDebugScript, "-> ofs = %08X\n", temp);
_runningFunction->jumpAbsolute(temp);
return true;
@@ -718,8 +716,8 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
if (value1.type != kInteger || value2.type != kInteger)
warning("ScriptInterpreter::execOpcode() Trying to compare non-integer values (%d, %d, line %d)", value1.type, value2.type, _lineNum);
_cmpFlags = value1.value - value2.value;
- printf("-> cmp %d, %d\n", value1.value, value2.value);
- printf("-> _cmpFlags = %d\n", _cmpFlags);
+ debugCN(kDebugScript, "-> cmp %d, %d\n", value1.value, value2.value);
+ debugCN(kDebugScript, "-> _cmpFlags = %d\n", _cmpFlags);
return true;
case opCall:
@@ -773,7 +771,7 @@ bool ScriptInterpreter::execOpcode(byte opcode) {
return true;
default:
- printf("Invalid opcode %d!\n", opcode);
+ debugCN(kDebugScript, "Invalid opcode %d!\n", opcode);
return false;
}
@@ -922,14 +920,14 @@ int ScriptInterpreter::o1_wilburFinishedTalking() {
int ScriptInterpreter::o1_preloadSound() {
const char *name = STRING(0);
int room = INTEGER(1);
- printf("name = %s; room = %d\n", name, room);
+ debugCN(kDebugScript, "name = %s; room = %d\n", name, room);
return 2;
}
int ScriptInterpreter::o1_unloadSound() {
const char *name = STRING(0);
int room = INTEGER(1);
- printf("name = %s; room = %d\n", name, room);
+ debugCN(kDebugScript, "name = %s; room = %d\n", name, room);
return 2;
}
@@ -939,7 +937,7 @@ int ScriptInterpreter::o1_playSound() {
int volume = INTEGER(2);
int trigger = INTEGER(3);
int room = INTEGER(4);
- printf("name = %s; channel = %d; volume = %d; trigger = %d; room = %d\n",
+ debugCN(kDebugScript, "name = %s; channel = %d; volume = %d; trigger = %d; room = %d\n",
name, channel, volume, trigger, room);
Common::String soundName = Common::String(name) + ".raw";
@@ -957,7 +955,7 @@ int ScriptInterpreter::o1_playLoopingSound() {
int volume = INTEGER(2);
int trigger = INTEGER(3);
int room = INTEGER(4);
- printf("name = %s; channel = %d; volume = %d; trigger = %d; room = %d\n",
+ debugCN(kDebugScript, "name = %s; channel = %d; volume = %d; trigger = %d; room = %d\n",
name, channel, volume, trigger, room);
// HACK until fixed
@@ -968,14 +966,14 @@ int ScriptInterpreter::o1_playLoopingSound() {
int ScriptInterpreter::o1_stopSound() {
int channel = INTEGER(0);
- printf("channel = %d\n", channel);
+ debugCN(kDebugScript, "channel = %d\n", channel);
return 1;
}
int ScriptInterpreter::o1_fadeSetStart() {
// skip arg 0: palette ptr
int percent = INTEGER(1);
- printf("percent = %d\n", percent);
+ debugCN(kDebugScript, "percent = %d\n", percent);
return 2;
}
@@ -986,7 +984,7 @@ int ScriptInterpreter::o1_fadeInit() {
int percent = INTEGER(3);
int ticks = INTEGER(4);
int trigger = INTEGER(5);
- printf("first = %d; last = %d; percent = %d; ticks = %d; trigger = %d\n",
+ debugCN(kDebugScript, "first = %d; last = %d; percent = %d; ticks = %d; trigger = %d\n",
first, last, percent, ticks, trigger);
// HACK until palette fading is implemented
@@ -1005,7 +1003,7 @@ int ScriptInterpreter::o1_initPaletteCycle() {
int delay = INTEGER(2);
int ticks = INTEGER(3);
int trigger = INTEGER(4);
- printf("first = %d; last = %d; delay = %d; ticks = %d; trigger = %d\n",
+ debugCN(kDebugScript, "first = %d; last = %d; delay = %d; ticks = %d; trigger = %d\n",
first, last, delay, ticks, trigger);
// HACK until palette cycling is implemented
@@ -1022,12 +1020,11 @@ int ScriptInterpreter::o1_hasPlayerSaid() {
const char *words[3];
for (int i = 0; i < 3; i++)
words[i] = STRING(i);
- printf("'%s', '%s', '%s'\n", words[0], words[1], words[2]);
+ debugCN(kDebugScript, "'%s', '%s', '%s'\n", words[0], words[1], words[2]);
int result = _vm->_player->said(words[0], words[1], words[2]);
- printf(" -> '%d'\n", result);
- fflush(stdout);
+ debugCN(kDebugScript, " -> '%d'\n", result);
RETURN(result);
return 3;
@@ -1038,12 +1035,11 @@ int ScriptInterpreter::o1_hasPlayerSaidAny() {
for (int i = 0; i < 10; i++)
words[i] = STRING(i);
- printf("'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'\n",
+ debugCN(kDebugScript, "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'\n",
words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
int result = _vm->_player->saidAny(words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
- printf(" -> '%d'\n", result);
- fflush(stdout);
+ debugCN(kDebugScript, " -> '%d'\n", result);
RETURN(result);
return 10;
@@ -1059,13 +1055,13 @@ int ScriptInterpreter::o1_playerHotspotWalkOverride() {
int y1 = INTEGER(1);
int x2 = INTEGER(2);
int y2 = INTEGER(3);
- printf("(%d, %d); (%d, %d)\n", x1, y1, x2, y2);
+ debugCN(kDebugScript, "(%d, %d); (%d, %d)\n", x1, y1, x2, y2);
return 4;
}
int ScriptInterpreter::o1_playerHasItem() {
const char *name = STRING(0);
- printf("item = '%s'\n", name);
+ debugCN(kDebugScript, "item = '%s'\n", name);
// TODO
RETURN(0);
return 1;
@@ -1075,14 +1071,14 @@ int ScriptInterpreter::o1_setWalkerLocation() {
// skip arg 0: walker
int x = INTEGER(1);
int y = INTEGER(2);
- printf("x = %d; y = %d\n", x, y);
+ debugCN(kDebugScript, "x = %d; y = %d\n", x, y);
return 3;
}
int ScriptInterpreter::o1_setWalkerFacing() {
// skip arg 0: walker
int facing = INTEGER(1);
- printf("facing = %d\n", facing);
+ debugCN(kDebugScript, "facing = %d\n", facing);
return 2;
}
@@ -1090,7 +1086,7 @@ int ScriptInterpreter::o1_setHotspot() {
// skip arg 0: hotspot list
const char *name = STRING(1);
int value = INTEGER(2);
- printf("name = '%s' -> %d\n", name, value);
+ debugCN(kDebugScript, "name = '%s' -> %d\n", name, value);
_vm->_scene->getSceneResources().hotspots->setActive(name, (value != 0));
@@ -1121,9 +1117,8 @@ int ScriptInterpreter::o1_playSeries() {
int firstFrame = INTEGER(9);
int lastFrame = INTEGER(10);
- printf("name = %s; layer = %04X; flags = %08X; trigger = %d; frameRate = %d; loopCount = %d; scale = %d; x = %d; y = %d: firstFrame = %d; lastFrame = %d\n",
+ debugCN(kDebugScript, "name = %s; layer = %04X; flags = %08X; trigger = %d; frameRate = %d; loopCount = %d; scale = %d; x = %d; y = %d: firstFrame = %d; lastFrame = %d\n",
name, layer, flags, trigger, frameRate, loopCount, scale, x, y, firstFrame, lastFrame);
- fflush(stdout);
// TODO: Return the machine to the script
_vm->_ws->playSeries(name, layer, flags, trigger, frameRate, loopCount, scale, x, y, firstFrame, lastFrame);
@@ -1142,9 +1137,8 @@ int ScriptInterpreter::o1_showSeries() {
int x = INTEGER(7);
int y = INTEGER(8);
- printf("name = %s; layer = %04X; flags = %08X; trigger = %d; duration = %d; frameIndex = %d; scale = %d; x = %d; y = %d\n",
+ debugCN(kDebugScript, "name = %s; layer = %04X; flags = %08X; trigger = %d; duration = %d; frameIndex = %d; scale = %d; x = %d; y = %d\n",
name, layer, flags, trigger, duration, frameIndex, scale, x, y);
- fflush(stdout);
// TODO: Return the machine to the script
_vm->_ws->showSeries(name, layer, flags, trigger, duration, frameIndex, scale, x, y);
@@ -1157,8 +1151,7 @@ int ScriptInterpreter::o1_loadSeries() {
int hash = INTEGER(1);
// skip arg 3: palette ptr
- printf("name = %s; hash = %d\n", name, hash);
- fflush(stdout);
+ debugCN(kDebugScript, "name = %s; hash = %d\n", name, hash);
int result = _vm->_ws->loadSeries(name, hash, NULL);
@@ -1189,7 +1182,7 @@ int ScriptInterpreter::o1_globalTriggerProc() {
int value1 = INTEGER(0);
int value2 = INTEGER(1);
int value3 = INTEGER(2);
- printf("%d; %d; %d\n", value1, value2, value3);
+ debugCN(kDebugScript, "%d; %d; %d\n", value1, value2, value3);
return 3;
}
@@ -1197,13 +1190,13 @@ int ScriptInterpreter::o1_triggerTimerProc() {
int value1 = INTEGER(0);
int value2 = INTEGER(1);
int value3 = INTEGER(2);
- printf("%d; %d; %d\n", value1, value2, value3);
+ debugCN(kDebugScript, "%d; %d; %d\n", value1, value2, value3);
return 3;
}
int ScriptInterpreter::o1_dispatchTrigger() {
int trigger = INTEGER(0);
- printf("trigger = %d\n", trigger);
+ debugCN(kDebugScript, "trigger = %d\n", trigger);
_vm->_kernel->sendTrigger(trigger);
//g_system->delayMillis(5000);
@@ -1229,7 +1222,7 @@ int ScriptInterpreter::o1_wilburSaid() {
SaidArrayItem *item = saidArray[i];
if (_vm->_player->said("LOOK AT", item->itemName) && item->digiNameLook) {
- printf(" -> LOOK AT: '%s'\n", item->digiNameLook);
+ debugCN(kDebugScript, " -> LOOK AT: '%s'\n", item->digiNameLook);
Common::String soundName = Common::String(item->digiNameLook) + ".raw";
_vm->_sound->playVoice(soundName.c_str(), 100);
result = 1;
@@ -1237,7 +1230,7 @@ int ScriptInterpreter::o1_wilburSaid() {
}
if (_vm->_player->said("TAKE", item->itemName) && item->digiNameTake) {
- printf(" -> TAKE: '%s'\n", item->digiNameTake);
+ debugCN(kDebugScript, " -> TAKE: '%s'\n", item->digiNameTake);
Common::String soundName = Common::String(item->digiNameTake) + ".raw";
_vm->_sound->playVoice(soundName.c_str(), 100);
result = 1;
@@ -1245,7 +1238,7 @@ int ScriptInterpreter::o1_wilburSaid() {
}
if (_vm->_player->said("GEAR", item->itemName) && item->digiNameGear) {
- printf(" -> GEAR: '%s'\n", item->digiNameGear);
+ debugCN(kDebugScript, " -> GEAR: '%s'\n", item->digiNameGear);
Common::String soundName = Common::String(item->digiNameGear) + ".raw";
_vm->_sound->playVoice(soundName.c_str(), 100);
result = 1;
@@ -1253,12 +1246,11 @@ int ScriptInterpreter::o1_wilburSaid() {
}
/*
- printf("##### itemName = '%s'; digiNameLook = %s; digiNameTake = %s; digiNameGear = %s\n",
+ debugCN(kDebugScript, "##### itemName = '%s'; digiNameLook = %s; digiNameTake = %s; digiNameGear = %s\n",
item->itemName, item->digiNameLook, item->digiNameTake, item->digiNameGear);
*/
}
- printf(" -> '%d'\n", result);
- fflush(stdout);
+ debugCN(kDebugScript, " -> '%d'\n", result);
RETURN(result);
return 1;
@@ -1278,8 +1270,7 @@ int ScriptInterpreter::o1_wilburSpeech() {
int volume = INTEGER(4);
int slot = INTEGER(5);
- printf("%s; %d; %d; %d; %d; %d\n", name, trigger, room, flag, volume, slot);
- fflush(stdout);
+ debugCN(kDebugScript, "%s; %d; %d; %d; %d; %d\n", name, trigger, room, flag, volume, slot);
//g_system->delayMillis(5000);
KernelTriggerType oldTriggerMode = _vm->_kernel->triggerMode;
@@ -1297,14 +1288,14 @@ int ScriptInterpreter::o1_wilburSpeech() {
void ScriptInterpreter::getKernelVar(int index, ScriptValue &value) {
- printf("ScriptInterpreter::getKernelVar() index = %d\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::getKernelVar() index = %d\n", index);
if (index > _kernelVarsMax) {
- printf("ScriptInterpreter::getKernelVar() Invalid kernel var index %d!\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::getKernelVar() Invalid kernel var index %d!\n", index);
return;
}
- printf("ScriptInterpreter::getKernelVar() name = %s\n", _kernelVars[index].desc);
+ debugCN(kDebugScript, "ScriptInterpreter::getKernelVar() name = %s\n", _kernelVars[index].desc);
ScriptKernelVariable var = _kernelVars[index].var;
@@ -1342,7 +1333,7 @@ void ScriptInterpreter::getKernelVar(int index, ScriptValue &value) {
break;
default:
- printf("ScriptInterpreter::getKernelVar() Invalid kernel var %d!\n", var);
+ debugCN(kDebugScript, "ScriptInterpreter::getKernelVar() Invalid kernel var %d!\n", var);
//g_system->delayMillis(2000);
}
@@ -1351,14 +1342,14 @@ void ScriptInterpreter::getKernelVar(int index, ScriptValue &value) {
void ScriptInterpreter::setKernelVar(int index, const ScriptValue &value) {
- printf("ScriptInterpreter::setKernelVar() index = %d\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::setKernelVar() index = %d\n", index);
if (index > _kernelVarsMax) {
- printf("ScriptInterpreter::setKernelVar() Invalid kernel var index %d!\n", index);
+ debugCN(kDebugScript, "ScriptInterpreter::setKernelVar() Invalid kernel var index %d!\n", index);
return;
}
- printf("ScriptInterpreter::setKernelVar() name = %s\n", _kernelVars[index].desc);
+ debugCN(kDebugScript, "ScriptInterpreter::setKernelVar() name = %s\n", _kernelVars[index].desc);
ScriptKernelVariable var = _kernelVars[index].var;
@@ -1366,31 +1357,31 @@ void ScriptInterpreter::setKernelVar(int index, const ScriptValue &value) {
case kKernelTrigger:
_vm->_kernel->trigger = toInteger(value);
- printf("kKernelTrigger -> %d\n", toInteger(value));
+ debugCN(kDebugScript, "kKernelTrigger -> %d\n", toInteger(value));
break;
case kKernelTriggerMode:
_vm->_kernel->triggerMode = (KernelTriggerType)toInteger(value);
- printf("kKernelTrigger -> %d\n", toInteger(value));
+ debugCN(kDebugScript, "kKernelTrigger -> %d\n", toInteger(value));
break;
case kKernelContinueHandlingTrigger:
_vm->_kernel->daemonTriggerAvailable = (toInteger(value) != 0);
- printf("kKernelContinueHandlingTrigger -> %d\n", toInteger(value));
+ debugCN(kDebugScript, "kKernelContinueHandlingTrigger -> %d\n", toInteger(value));
break;
case kGameNewRoom:
_vm->_kernel->newRoom = toInteger(value);
- printf("kGameNewRoom -> %d\n", toInteger(value));
+ debugCN(kDebugScript, "kGameNewRoom -> %d\n", toInteger(value));
break;
case kPlayerCommandReady:
// TODO
- printf("kPlayerCommandReady -> %d\n", toInteger(value));
+ debugCN(kDebugScript, "kPlayerCommandReady -> %d\n", toInteger(value));
break;
default:
- printf("ScriptInterpreter::setKernelVar() Invalid kernel var %d!\n", var);
+ debugCN(kDebugScript, "ScriptInterpreter::setKernelVar() Invalid kernel var %d!\n", var);
//g_system->delayMillis(2000);
}
diff --git a/engines/m4/script.h b/engines/m4/script.h
index 7382c05b67..32b5701419 100644
--- a/engines/m4/script.h
+++ b/engines/m4/script.h
@@ -223,7 +223,7 @@ public:
uint32 readUint32();
protected:
ScriptInterpreter *_inter;
- Common::MemoryReadStream *_code;
+ Common::SeekableReadStream *_code;
};
struct ScriptFunctionEntry {
@@ -305,7 +305,7 @@ public:
// Is this ok?
template<class T>
const T& toData(const ScriptValue &value) {
- printf("ScriptInterpreter::toData() index = %d; type = %d; max = %d\n", value.value, _data[value.value]->type, _data.size());
+ debugCN(kDebugScript, "ScriptInterpreter::toData() index = %d; type = %d; max = %d\n", value.value, _data[value.value]->type, _data.size());
assert((uint32)value.value < _data.size());
T *result = 0;
_dataCache->load(_scriptFile, _data[value.value]->offset, result);
diff --git a/engines/m4/sound.cpp b/engines/m4/sound.cpp
index e0fbd2f7a9..76f0cded46 100644
--- a/engines/m4/sound.cpp
+++ b/engines/m4/sound.cpp
@@ -194,7 +194,7 @@ void Sound::loadDSRFile(const char *fileName) {
// Read header
_dsrFile.entryCount = fileStream->readUint16LE();
- //printf("DSR has %i entries\n", _dsrFile.entryCount);
+ //warning(kDebugSound, "DSR has %i entries\n", _dsrFile.entryCount);
for (int i = 0; i < _dsrFile.entryCount; i++) {
DSREntry newEntry;
@@ -206,13 +206,13 @@ void Sound::loadDSRFile(const char *fileName) {
_dsrFile.dsrEntries.push_back(newEntry);
/*
- printf("%i: ", i);
- printf("frequency: %i ", newEntry->frequency);
- printf("channels: %i ", newEntry->channels);
- printf("comp: %i ", newEntry.compSize);
- printf("uncomp: %i ", newEntry.uncompSize);
- printf("offset: %i ", newEntry->offset);
- printf("\n");
+ warning(kDebugSound, "%i: ", i);
+ warning(kDebugSound, "frequency: %i ", newEntry->frequency);
+ warning(kDebugSound, "channels: %i ", newEntry->channels);
+ warning(kDebugSound, "comp: %i ", newEntry.compSize);
+ warning(kDebugSound, "uncomp: %i ", newEntry.uncompSize);
+ warning(kDebugSound, "offset: %i ", newEntry->offset);
+ warning(kDebugSound, "\n");
*/
}
diff --git a/engines/m4/woodscript.cpp b/engines/m4/woodscript.cpp
index 3ce0fa0f2f..4928e0af8d 100644
--- a/engines/m4/woodscript.cpp
+++ b/engines/m4/woodscript.cpp
@@ -25,6 +25,8 @@
#include "m4/woodscript.h"
+#include "common/memstream.h"
+
namespace M4 {
// FIXME: Put in Engine/WoodScript class
@@ -46,7 +48,7 @@ Bytecode::~Bytecode() {
int Bytecode::loadInstruction(Instruction &instruction) {
- //printf("Bytecode::loadInstruction() ip = %08X\n", _code->pos());
+ //debugCN(kDebugScript, "Bytecode::loadInstruction() ip = %08X\n", _code->pos());
int32 format, data;
uint32 code, code2;
@@ -90,7 +92,7 @@ int Bytecode::loadInstruction(Instruction &instruction) {
void Bytecode::jumpAbsolute(int32 ofs) {
_code->seek(ofs * 4);
- //printf("Bytecode::jumpAbsolute() ofs = %08X\n", _code->pos());
+ //debugCN(kDebugScript, "Bytecode::jumpAbsolute() ofs = %08X\n", _code->pos());
}
void Bytecode::jumpRelative(int32 ofs) {
@@ -200,7 +202,7 @@ void WoodScript::runTimerSequenceRequests() {
Machine *WoodScript::createMachine(int32 machineHash, Sequence *parentSeq,
int32 dataHash, int32 dataRowIndex, int callbackHandler, const char *machineName) {
- //printf("WoodScript::createMachine(%d)\n", machineHash); fflush(stdout);
+ //debugCN(kDebugScript, "WoodScript::createMachine(%d)\n", machineHash);
Machine *machine = new Machine(this, machineHash, parentSeq, dataHash, dataRowIndex, callbackHandler, machineName, _machineId);
_machineId++;
@@ -228,7 +230,7 @@ Machine *WoodScript::playSeries(const char *seriesName, long layer, uint32 flags
int32 frameRate, int32 loopCount, int32 s, int32 x, int32 y,
int32 firstFrame, int32 lastFrame) {
- //printf("WoodScript::playSeries(%s)\n", seriesName);
+ //debugCN(kDebugScript, "WoodScript::playSeries(%s)\n", seriesName);
RGB8 *palette = NULL;
if (flags & SERIES_LOAD_PALETTE)
@@ -282,7 +284,7 @@ Machine *WoodScript::showSeries(const char *seriesName, long layer, uint32 flags
}
Machine *WoodScript::streamSeries(const char *seriesName, int32 frameRate, long layer, int32 triggerNum) {
- //printf("WoodScript::streamSeries(%s)\n", seriesName);
+ //debugCN(kDebugScript, "WoodScript::streamSeries(%s)\n", seriesName);
_globals[kGlobTemp1] = frameRate << 16;
/* FIXME: Single frames from a stream series will be decompressed on-the-fly, contrary to
"normal" sprite series, to save some memory, and since no random access to single
diff --git a/engines/m4/woodscript.h b/engines/m4/woodscript.h
index 0f72935f8d..4b0f457193 100644
--- a/engines/m4/woodscript.h
+++ b/engines/m4/woodscript.h
@@ -77,7 +77,7 @@ public:
uint32 pos() const { return _code->pos() / 4; }
protected:
WoodScript *_ws;
- Common::MemoryReadStream *_code;
+ Common::SeekableReadStream *_code;
Sequence *_sequence;
static int32 _dataFormats[];
bool decodeArgument(int32 format, int32 data, long *&arg, long &value);
diff --git a/engines/m4/ws_machine.cpp b/engines/m4/ws_machine.cpp
index cb791026c9..3a25e3c622 100644
--- a/engines/m4/ws_machine.cpp
+++ b/engines/m4/ws_machine.cpp
@@ -147,7 +147,7 @@ void Machine::enterState() {
int32 Machine::execInstruction() {
- //printf("Machine::execInstruction()\n"); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::execInstruction()\n");
bool done = false;
Instruction instruction;
@@ -160,12 +160,16 @@ int32 Machine::execInstruction() {
if (machineConditionalsTable[instruction.instr - 64] != 0)
(this->*machineConditionalsTable[instruction.instr - 64])(instruction);
/* The next line is to yield on unimplemented opcodes */
- else { fflush(stdout); g_system->delayMillis(5000); }
+ else {
+ g_system->delayMillis(5000);
+ }
} else if (instruction.instr > 0) {
if (machineCommandsTable[instruction.instr] != 0)
done = !(this->*machineCommandsTable[instruction.instr])(instruction);
/* The next line is to yield on unimplemented opcodes */
- else { fflush(stdout); g_system->delayMillis(5000); }
+ else {
+ g_system->delayMillis(5000);
+ }
if (done) {
if (_id == machID) {
//TODO: Cancel all requests
@@ -199,7 +203,7 @@ void Machine::execBlock(int32 offset, int32 count) {
int32 instruction = -1;
- //printf("---------------------------------------\n"); fflush(stdout);
+ //debugCN(kDebugScript, "---------------------------------------\n");
while (instruction && instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel &&
_code->pos() >= (uint32)startOffset && _code->pos() < (uint32)endOffset) {
@@ -208,7 +212,7 @@ void Machine::execBlock(int32 offset, int32 count) {
//g_system->delayMillis(500);
}
- //printf("---------------------------------------\n"); fflush(stdout);
+ //debugCN(kDebugScript, "---------------------------------------\n");
if (instruction == 3) {
execInstruction();
@@ -221,7 +225,7 @@ void Machine::execBlock(int32 offset, int32 count) {
}
bool Machine::m1_gotoState(Instruction &instruction) {
- //printf("Machine::m1_gotoState() state = %d\n", (int32)instruction.argv[0] >> 16);
+ //debugCN(kDebugScript, "Machine::m1_gotoState() state = %d\n", (int32)instruction.argv[0] >> 16);
_currentState = (int32)instruction.argv[0] >> 16;
_recursionLevel = 0;
@@ -229,14 +233,14 @@ bool Machine::m1_gotoState(Instruction &instruction) {
}
bool Machine::m1_jump(Instruction &instruction) {
- //printf("Machine::m1_jump() ofs = %08X\n", (int32)instruction.argv[0] >> 16);
+ //debugCN(kDebugScript, "Machine::m1_jump() ofs = %08X\n", (int32)instruction.argv[0] >> 16);
_code->jumpRelative((int32)instruction.argv[0] >> 16);
return true;
}
bool Machine::m1_terminate(Instruction &instruction) {
- //printf("Machine::m1_terminate()\n"); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_terminate()\n");
_currentState = -1;
_recursionLevel = 0;
@@ -244,15 +248,15 @@ bool Machine::m1_terminate(Instruction &instruction) {
}
bool Machine::m1_startSequence(Instruction &instruction) {
- //printf("Machine::m1_startSequence() sequence hash = %d\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_startSequence() sequence hash = %d\n", (uint32)instruction.argv[0] >> 16);
int32 sequenceHash = instruction.argv[0] >> 16;
if (_sequence == NULL) {
- //printf("Machine::m1_startSequence() creating new sequence\n");
+ //debugCN(kDebugScript, "Machine::m1_startSequence() creating new sequence\n");
_sequence = _ws->createSequence(this, sequenceHash);
_code->setSequence(_sequence);
} else {
- //printf("Machine::m1_startSequence() using existing sequence\n");
+ //debugCN(kDebugScript, "Machine::m1_startSequence() using existing sequence\n");
_sequence->changeProgram(sequenceHash);
//_code->setSequence(_sequence);
}
@@ -260,28 +264,28 @@ bool Machine::m1_startSequence(Instruction &instruction) {
}
bool Machine::m1_pauseSequence(Instruction &instruction) {
- //printf("Machine::m1_pauseSequence()\n"); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_pauseSequence()\n");
_sequence->pause();
return true;
}
bool Machine::m1_resumeSequence(Instruction &instruction) {
- //printf("Machine::m1_resumeSequence()\n"); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_resumeSequence()\n");
_sequence->resume();
return true;
}
bool Machine::m1_storeValue(Instruction &instruction) {
- //printf("Machine::m1_storeValue() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
+ //debugCN(kDebugScript, "Machine::m1_storeValue() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
*instruction.argp[0] = instruction.getValue();
return true;
}
bool Machine::m1_sendMessage(Instruction &instruction) {
- //printf("Machine::m1_sendMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
+ //debugCN(kDebugScript, "Machine::m1_sendMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
#if 0
//TODO
@@ -300,7 +304,7 @@ bool Machine::m1_sendMessage(Instruction &instruction) {
}
bool Machine::m1_broadcastMessage(Instruction &instruction) {
- //printf("Machine::m1_broadcastMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
+ //debugCN(kDebugScript, "Machine::m1_broadcastMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
#if 0
//TODO
@@ -317,7 +321,7 @@ bool Machine::m1_broadcastMessage(Instruction &instruction) {
}
bool Machine::m1_replyMessage(Instruction &instruction) {
- //printf("Machine::m1_replyMessage() messageHash = %d; messageValue = %d\n", (uint32)instruction.argv[0], (uint32)instruction.argv[1]);
+ //debugCN(kDebugScript, "Machine::m1_replyMessage() messageHash = %d; messageValue = %d\n", (uint32)instruction.argv[0], (uint32)instruction.argv[1]);
#if 0
if (myArg2) {
msgValue = *myArg2;
@@ -331,28 +335,28 @@ bool Machine::m1_replyMessage(Instruction &instruction) {
}
bool Machine::m1_sendSystemMessage(Instruction &instruction) {
- //printf("Machine::m1_sendSystemMessage() messageValue = %d\n", (uint32)instruction.argv[0]);
+ //debugCN(kDebugScript, "Machine::m1_sendSystemMessage() messageValue = %d\n", (uint32)instruction.argv[0]);
#if 0
#endif
return true;
}
bool Machine::m1_createMachine(Instruction &instruction) {
- //printf("Machine::m1_createMachine()\n");
+ //debugCN(kDebugScript, "Machine::m1_createMachine()\n");
#if 0
#endif
return true;
}
bool Machine::m1_createMachineEx(Instruction &instruction) {
- //printf("Machine::m1_createMachineEx()\n");
+ //debugCN(kDebugScript, "Machine::m1_createMachineEx()\n");
#if 0
#endif
return true;
}
bool Machine::m1_clearVars(Instruction &instruction) {
- //printf("Machine::m1_clearVars()\n"); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_clearVars()\n");
_sequence->clearVars();
return true;
@@ -360,7 +364,7 @@ bool Machine::m1_clearVars(Instruction &instruction) {
void Machine::m1_onEndSequence(Instruction &instruction) {
- //printf("Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16);
int32 count = instruction.argv[0] >> 16;
_sequence->issueEndOfSequenceRequest(_code->pos(), count);
@@ -368,7 +372,7 @@ void Machine::m1_onEndSequence(Instruction &instruction) {
}
void Machine::m1_onMessage(Instruction &instruction) {
- //printf("Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16);
// TODO: Add message to list
@@ -378,42 +382,42 @@ void Machine::m1_onMessage(Instruction &instruction) {
}
void Machine::m1_switchLt(Instruction &instruction) {
- //printf("Machine::m1_switchLt() %d < %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_switchLt() %d < %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16);
if (instruction.argv[1] >= instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchLe(Instruction &instruction) {
- //printf("Machine::m1_switchLe() %d <= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_switchLe() %d <= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16);
if (instruction.argv[1] > instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchEq(Instruction &instruction) {
- //printf("Machine::m1_switchEq() %d == %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_switchEq() %d == %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16);
if (instruction.argv[1] != instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchNe(Instruction &instruction) {
- //printf("Machine::m1_switchNe() %d != %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_switchNe() %d != %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16);
if (instruction.argv[1] == instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchGe(Instruction &instruction) {
- //printf("Machine::m1_switchGe() %d >= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_switchGe() %d >= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16);
if (instruction.argv[1] < instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchGt(Instruction &instruction) {
- //printf("Machine::m1_switchGt() %d > %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "Machine::m1_switchGt() %d > %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16);
if (instruction.argv[1] <= instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
diff --git a/engines/m4/ws_sequence.cpp b/engines/m4/ws_sequence.cpp
index 79869a81d0..bb6230d041 100644
--- a/engines/m4/ws_sequence.cpp
+++ b/engines/m4/ws_sequence.cpp
@@ -201,7 +201,7 @@ void Sequence::resume() {
void Sequence::issueEndOfSequenceRequest(int32 codeOffset, int32 count) {
- //printf("Sequence::issueEndOfSequenceRequest(%04X, %04X)\n", codeOffset, count); fflush(stdout);
+ //debugCN(kDebugScript, "Sequence::issueEndOfSequenceRequest(%04X, %04X)\n", codeOffset, count);
//g_system->delayMillis(5000);
_endOfSequenceRequest.codeOffset = codeOffset;
@@ -216,7 +216,7 @@ bool Sequence::runProgram() {
bool done = true;
- //printf("_ws->getGlobal(kGlobTime) = %ld, _switchTime = %d\n", _ws->getGlobal(kGlobTime), _switchTime);
+ //debugCN(kDebugScript, "_ws->getGlobal(kGlobTime) = %ld, _switchTime = %d\n", _ws->getGlobal(kGlobTime), _switchTime);
if (_switchTime >= 0 && _ws->getGlobal(kGlobTime) >= _switchTime)
done = false;
@@ -228,7 +228,9 @@ bool Sequence::runProgram() {
_code->loadInstruction(instruction);
if (sequenceCommandsTable[instruction.instr] != 0)
done = !(this->*sequenceCommandsTable[instruction.instr])(instruction);
- else { fflush(stdout); /*g_system->delayMillis(1000);*/ }
+ else {
+ //g_system->delayMillis(1000);
+ }
}
return _terminated;
@@ -246,7 +248,7 @@ bool Sequence::changeProgram(int32 sequenceHash) {
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(sequenceHash);
if (sequenceAsset->localVarCount() > _localVarCount) {
- //printf("Sequence::changeProgram(%d) sequenceAsset->localVarCount() > _localVarCount\n", sequenceHash);
+ //debugCN(kDebugScript, "Sequence::changeProgram(%d) sequenceAsset->localVarCount() > _localVarCount\n", sequenceHash);
return false;
}
@@ -301,14 +303,14 @@ void Sequence::draw(M4Surface *surface, const Common::Rect &clipRect, Common::Re
}
bool Sequence::s1_end(Instruction &instruction) {
- //printf("Sequence::s1_end()\n");
+ //debugCN(kDebugScript, "Sequence::s1_end()\n");
_terminated = true;
return false;
}
bool Sequence::s1_clearVars(Instruction &instruction) {
- //printf("Sequence::s1_clearVars()\n");
+ //debugCN(kDebugScript, "Sequence::s1_clearVars()\n");
clearVars();
_vars[kSeqVarMachineID] = _machine->getId();
@@ -316,14 +318,14 @@ bool Sequence::s1_clearVars(Instruction &instruction) {
}
bool Sequence::s1_set(Instruction &instruction) {
- //printf("Sequence::s1_set()\n");
+ //debugCN(kDebugScript, "Sequence::s1_set()\n");
*instruction.argp[0] = instruction.getValue();
return true;
}
bool Sequence::s1_compare(Instruction &instruction) {
- //printf("Sequence::s1_compare()\n");
+ //debugCN(kDebugScript, "Sequence::s1_compare()\n");
long value = instruction.getValue();
if (instruction.argv[0] < value)
@@ -336,28 +338,28 @@ bool Sequence::s1_compare(Instruction &instruction) {
}
bool Sequence::s1_add(Instruction &instruction) {
- //printf("Sequence::s1_add()\n");
+ //debugCN(kDebugScript, "Sequence::s1_add()\n");
*instruction.argp[0] += instruction.getValue();
return true;
}
bool Sequence::s1_sub(Instruction &instruction) {
- //printf("Sequence::s1_sub()\n");
+ //debugCN(kDebugScript, "Sequence::s1_sub()\n");
*instruction.argp[0] -= instruction.getValue();
return true;
}
bool Sequence::s1_mul(Instruction &instruction) {
- //printf("Sequence::s1_mul()\n");
+ //debugCN(kDebugScript, "Sequence::s1_mul()\n");
*instruction.argp[0] = FixedMul(instruction.argv[0], instruction.getValue());
return true;
}
bool Sequence::s1_div(Instruction &instruction) {
- //printf("Sequence::s1_div()\n");
+ //debugCN(kDebugScript, "Sequence::s1_div()\n");
// TODO: Catch divisor = 0 in FixedDiv
*instruction.argp[0] = FixedDiv(instruction.argv[0], instruction.getValue());
@@ -365,7 +367,7 @@ bool Sequence::s1_div(Instruction &instruction) {
}
bool Sequence::s1_and(Instruction &instruction) {
- //printf("Sequence::s1_and()\n");
+ //debugCN(kDebugScript, "Sequence::s1_and()\n");
*instruction.argp[0] = instruction.argv[0] & instruction.getValue();
if (*instruction.argp[0])
@@ -376,7 +378,7 @@ bool Sequence::s1_and(Instruction &instruction) {
}
bool Sequence::s1_or(Instruction &instruction) {
- //printf("Sequence::s1_or()\n");
+ //debugCN(kDebugScript, "Sequence::s1_or()\n");
*instruction.argp[0] = instruction.argv[0] | instruction.getValue();
if (*instruction.argp[0])
@@ -387,7 +389,7 @@ bool Sequence::s1_or(Instruction &instruction) {
}
bool Sequence::s1_not(Instruction &instruction) {
- //printf("Sequence::s1_not()\n");
+ //debugCN(kDebugScript, "Sequence::s1_not()\n");
if (instruction.argv[0] == 0) {
*instruction.argp[0] = 0x10000;
@@ -400,7 +402,7 @@ bool Sequence::s1_not(Instruction &instruction) {
}
bool Sequence::s1_sin(Instruction &instruction) {
- //printf("Sequence::s1_sin()\n");
+ //debugCN(kDebugScript, "Sequence::s1_sin()\n");
int32 tempAngle = *instruction.argp[1] >> 16;
if (tempAngle < 0)
@@ -417,7 +419,7 @@ bool Sequence::s1_sin(Instruction &instruction) {
}
bool Sequence::s1_cos(Instruction &instruction) {
- //printf("Sequence::s1_cos()\n");
+ //debugCN(kDebugScript, "Sequence::s1_cos()\n");
int32 tempAngle = *instruction.argp[1] >> 16;
if (tempAngle < 0)
@@ -434,42 +436,42 @@ bool Sequence::s1_cos(Instruction &instruction) {
}
bool Sequence::s1_abs(Instruction &instruction) {
- //printf("Sequence::s1_abs()\n");
+ //debugCN(kDebugScript, "Sequence::s1_abs()\n");
*instruction.argp[0] = ABS(instruction.argv[1]);
return true;
}
bool Sequence::s1_min(Instruction &instruction) {
- //printf("Sequence::s1_min()\n");
+ //debugCN(kDebugScript, "Sequence::s1_min()\n");
*instruction.argp[0] = MIN(instruction.argv[1], instruction.argv[2]);
return true;
}
bool Sequence::s1_max(Instruction &instruction) {
- //printf("Sequence::s1_max()\n");
+ //debugCN(kDebugScript, "Sequence::s1_max()\n");
*instruction.argp[0] = MAX(instruction.argv[1], instruction.argv[2]);
return true;
}
bool Sequence::s1_mod(Instruction &instruction) {
- //printf("Sequence::s1_mod()\n");
+ //debugCN(kDebugScript, "Sequence::s1_mod()\n");
*instruction.argp[0] = instruction.argv[0] % instruction.getValue();
return true;
}
bool Sequence::s1_floor(Instruction &instruction) {
- //printf("Sequence::s1_floor()\n");
+ //debugCN(kDebugScript, "Sequence::s1_floor()\n");
*instruction.argp[0] = instruction.getValue() & 0xffff0000;
return true;
}
bool Sequence::s1_round(Instruction &instruction) {
- //printf("Sequence::s1_round()\n");
+ //debugCN(kDebugScript, "Sequence::s1_round()\n");
if ((*instruction.argp[1] & 0xffff) >= 0x8000)
*instruction.argp[0] = (*instruction.argp[1] + 0x10000) & 0xffff0000;
@@ -479,7 +481,7 @@ bool Sequence::s1_round(Instruction &instruction) {
}
bool Sequence::s1_ceil(Instruction &instruction) {
- //printf("Sequence::s1_ceil()\n");
+ //debugCN(kDebugScript, "Sequence::s1_ceil()\n");
if ((*instruction.argp[1] & 0xffff) >= 0)
*instruction.argp[0] = (*instruction.argp[1] + 0x10000) & 0xffff0000;
@@ -489,19 +491,19 @@ bool Sequence::s1_ceil(Instruction &instruction) {
}
bool Sequence::s1_point(Instruction &instruction) {
- printf("Sequence::s1_point()\n");
+ debugCN(kDebugScript, "Sequence::s1_point()\n");
// TODO
return true;
}
bool Sequence::s1_dist2d(Instruction &instruction) {
- printf("Sequence::s1_dist2d()\n");
+ debugCN(kDebugScript, "Sequence::s1_dist2d()\n");
// TODO
return true;
}
bool Sequence::s1_crunch(Instruction &instruction) {
- //printf("Sequence::s1_crunch()\n");
+ //debugCN(kDebugScript, "Sequence::s1_crunch()\n");
long deltaTime;
@@ -515,12 +517,12 @@ bool Sequence::s1_crunch(Instruction &instruction) {
_startTime = _ws->getGlobal(kGlobTime);
- //printf("deltaTime = %ld\n", deltaTime >> 16); fflush(stdout);
+ //debugCN(kDebugScript, "deltaTime = %ld\n", deltaTime >> 16);
//g_system->delayMillis(5000);
if (deltaTime >= 0) {
_switchTime = _ws->getGlobal(kGlobTime) + (deltaTime >> 16);
- //printf("_ws->getGlobal(kGlobTime) = %ld\n", _ws->getGlobal(kGlobTime)); fflush(stdout);
+ //debugCN(kDebugScript, "_ws->getGlobal(kGlobTime) = %ld\n", _ws->getGlobal(kGlobTime));
//g_system->delayMillis(5000);
} else {
_switchTime = -1;
@@ -532,7 +534,7 @@ bool Sequence::s1_crunch(Instruction &instruction) {
}
bool Sequence::s1_branch(Instruction &instruction) {
- //printf("Sequence::s1_branch()\n");
+ //debugCN(kDebugScript, "Sequence::s1_branch()\n");
uint32 ofs = instruction.argv[1] >> 16;
switch (instruction.argv[0] >> 16) {
@@ -569,7 +571,7 @@ bool Sequence::s1_branch(Instruction &instruction) {
}
bool Sequence::s1_setFrame(Instruction &instruction) {
- //printf("Sequence::s1_setFrame()\n");
+ //debugCN(kDebugScript, "Sequence::s1_setFrame()\n");
int32 frameIndex;
if (instruction.argc == 3) {
@@ -580,8 +582,8 @@ bool Sequence::s1_setFrame(Instruction &instruction) {
frameIndex = (instruction.argv[0] & 0xFF0000) >> 16;
}
- //printf("Sequence::s1_setFrame() spriteHash = %d\n", (uint32)instruction.argv[0] >> 24);
- //printf("Sequence::s1_setFrame() frameIndex = %d\n", frameIndex);
+ //debugCN(kDebugScript, "Sequence::s1_setFrame() spriteHash = %d\n", (uint32)instruction.argv[0] >> 24);
+ //debugCN(kDebugScript, "Sequence::s1_setFrame() frameIndex = %d\n", frameIndex);
SpriteAsset *spriteAsset = _ws->assets()->getSprite((uint32)instruction.argv[0] >> 24);
_curFrame = spriteAsset->getFrame(frameIndex);
@@ -590,25 +592,25 @@ bool Sequence::s1_setFrame(Instruction &instruction) {
}
bool Sequence::s1_sendMessage(Instruction &instruction) {
- printf("Sequence::s1_sendMessage()\n");
+ debugCN(kDebugScript, "Sequence::s1_sendMessage()\n");
// TODO
return true;
}
bool Sequence::s1_push(Instruction &instruction) {
- printf("Sequence::s1_push()\n");
+ debugCN(kDebugScript, "Sequence::s1_push()\n");
// TODO
return true;
}
bool Sequence::s1_pop(Instruction &instruction) {
- printf("Sequence::s1_pop()\n");
+ debugCN(kDebugScript, "Sequence::s1_pop()\n");
// TODO
return true;
}
bool Sequence::s1_jumpSub(Instruction &instruction) {
- //printf("Sequence::s1_jumpSub()\n");
+ //debugCN(kDebugScript, "Sequence::s1_jumpSub()\n");
_returnHashes[_returnStackIndex] = _sequenceHash;
_returnOffsets[_returnStackIndex] = _code->pos();
@@ -628,7 +630,7 @@ bool Sequence::s1_jumpSub(Instruction &instruction) {
}
bool Sequence::s1_return(Instruction &instruction) {
- //printf("Sequence::s1_return()\n");
+ //debugCN(kDebugScript, "Sequence::s1_return()\n");
if (_returnStackIndex <= 0)
return s1_end(instruction);
@@ -652,7 +654,7 @@ bool Sequence::s1_return(Instruction &instruction) {
}
bool Sequence::s1_getFrameCount(Instruction &instruction) {
- //printf("Sequence::s1_getFrameCount()\n");
+ //debugCN(kDebugScript, "Sequence::s1_getFrameCount()\n");
SpriteAsset *spriteAsset = _ws->assets()->getSprite(instruction.argv[1] >> 24);
*instruction.argp[0] = spriteAsset->getCount() << 16;
@@ -660,7 +662,7 @@ bool Sequence::s1_getFrameCount(Instruction &instruction) {
}
bool Sequence::s1_getFrameRate(Instruction &instruction) {
- //printf("Sequence::s1_getFrameRate()\n");
+ //debugCN(kDebugScript, "Sequence::s1_getFrameRate()\n");
SpriteAsset *spriteAsset = _ws->assets()->getSprite(instruction.argv[1] >> 24);
*instruction.argp[0] = spriteAsset->getFrameRate();
@@ -668,37 +670,37 @@ bool Sequence::s1_getFrameRate(Instruction &instruction) {
}
bool Sequence::s1_getCelsPixSpeed(Instruction &instruction) {
- printf("Sequence::s1_getCelsPixSpeed()\n");
+ debugCN(kDebugScript, "Sequence::s1_getCelsPixSpeed()\n");
// TODO
return true;
}
bool Sequence::s1_setIndex(Instruction &instruction) {
- printf("Sequence::s1_setIndex()\n");
+ debugCN(kDebugScript, "Sequence::s1_setIndex()\n");
// TODO
return true;
}
bool Sequence::s1_setLayer(Instruction &instruction) {
- printf("Sequence::s1_setLayer()\n");
+ debugCN(kDebugScript, "Sequence::s1_setLayer()\n");
//TODO
return true;
}
bool Sequence::s1_setDepth(Instruction &instruction) {
- printf("Sequence::s1_setDepth()\n");
+ debugCN(kDebugScript, "Sequence::s1_setDepth()\n");
//TODO
return true;
}
bool Sequence::s1_setData(Instruction &instruction) {
- printf("Sequence::s1_setData()\n");
+ debugCN(kDebugScript, "Sequence::s1_setData()\n");
//TODO
return true;
}
bool Sequence::s1_openStream(Instruction &instruction) {
- //printf("Sequence::s1_openStream()\n");
+ //debugCN(kDebugScript, "Sequence::s1_openStream()\n");
_stream = _vm->res()->openFile(_machine->name().c_str());
streamOpen();
@@ -706,14 +708,14 @@ bool Sequence::s1_openStream(Instruction &instruction) {
}
bool Sequence::s1_streamNextFrame(Instruction &instruction) {
- //printf("Sequence::s1_streamNextFrame()\n");
+ //debugCN(kDebugScript, "Sequence::s1_streamNextFrame()\n");
streamNextFrame();
return true;
}
bool Sequence::s1_closeStream(Instruction &instruction) {
- printf("Sequence::s1_closeStream()\n");
+ debugCN(kDebugScript, "Sequence::s1_closeStream()\n");
//TODO
return true;
}
@@ -726,8 +728,7 @@ bool Sequence::streamOpen() {
_vars[kSeqVarSpriteFrameCount] = _streamSpriteAsset->getCount() << 16;
_vars[kSeqVarSpriteFrameRate] = _streamSpriteAsset->getFrameRate() << 16;
- //printf("Sequence::streamOpen() frames = %d; max = %d x %d\n", _streamSpriteAsset->getCount(), _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
- //fflush(stdout);
+ //debugCN(kDebugScript, "Sequence::streamOpen() frames = %d; max = %d x %d\n", _streamSpriteAsset->getCount(), _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
_curFrame = new M4Sprite(_vm, _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
streamNextFrame();
diff --git a/engines/made/console.cpp b/engines/made/console.cpp
new file mode 100644
index 0000000000..ee85c465e8
--- /dev/null
+++ b/engines/made/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "made/console.h"
+#include "made/made.h"
+
+namespace Made {
+
+MadeConsole::MadeConsole(MadeEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+MadeConsole::~MadeConsole() {
+}
+
+void MadeConsole::preEnter() {
+}
+
+void MadeConsole::postEnter() {
+}
+
+} // End of namespace Made
diff --git a/engines/made/console.h b/engines/made/console.h
new file mode 100644
index 0000000000..9f4632b986
--- /dev/null
+++ b/engines/made/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef MADE_CONSOLE_H
+#define MADE_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Made {
+
+class MadeEngine;
+
+class MadeConsole : public GUI::Debugger {
+public:
+ MadeConsole(MadeEngine *vm);
+ virtual ~MadeConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ MadeEngine *_vm;
+};
+
+} // End of namespace Made
+
+#endif
diff --git a/engines/made/database.cpp b/engines/made/database.cpp
index 51308cb7e5..2aa378edf5 100644
--- a/engines/made/database.cpp
+++ b/engines/made/database.cpp
@@ -275,7 +275,7 @@ void GameDatabase::openFromRed(const char *redFilename, const char *filename) {
_isRedSource = true;
_filename = filename;
_redFilename = redFilename;
- Common::MemoryReadStream *fileS = RedReader::loadFromRed(redFilename, filename);
+ Common::SeekableReadStream *fileS = RedReader::loadFromRed(redFilename, filename);
if (!fileS)
error("GameDatabase::openFromRed() Could not load %s from %s", filename, redFilename);
load(*fileS);
@@ -289,7 +289,7 @@ void GameDatabase::reload() {
error("GameDatabase::reload() Could not open %s", _filename.c_str());
reloadFromStream(fd);
} else {
- Common::MemoryReadStream *fileS = RedReader::loadFromRed(_redFilename.c_str(), _filename.c_str());
+ Common::SeekableReadStream *fileS = RedReader::loadFromRed(_redFilename.c_str(), _filename.c_str());
if (!fileS)
error("GameDatabase::openFromRed() Could not load %s from %s", _filename.c_str(), _redFilename.c_str());
reloadFromStream(*fileS);
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index 4b59723772..bb12e14e72 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -35,6 +35,8 @@
#include "engines/util.h"
+#include "backends/audiocd/audiocd.h"
+
#include "base/plugins.h"
#include "base/version.h"
@@ -78,6 +80,8 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
_rnd = new Common::RandomSource();
g_eventRec.registerRandomSource(*_rnd, "made");
+ _console = new MadeConsole(this);
+
int cd_num = ConfMan.getInt("cdrom");
if (cd_num >= 0)
_system->getAudioCDManager()->openCD(cd_num);
@@ -132,6 +136,7 @@ MadeEngine::~MadeEngine() {
_system->getAudioCDManager()->stop();
delete _rnd;
+ delete _console;
delete _pmvPlayer;
delete _res;
delete _screen;
@@ -233,6 +238,12 @@ void MadeEngine::handleEvents() {
if (_eventKey == Common::KEYCODE_BACKSPACE)
_eventKey = 9;
_eventNum = 5;
+
+ // Check for Debugger Activation
+ if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) {
+ this->getDebugger()->attach();
+ this->getDebugger()->onFrame();
+ }
break;
default:
diff --git a/engines/made/made.h b/engines/made/made.h
index 553476540a..302c8f5275 100644
--- a/engines/made/made.h
+++ b/engines/made/made.h
@@ -46,14 +46,18 @@
#include "engines/engine.h"
#include "made/sound.h"
+#include "made/console.h"
/**
* This is the namespace of the Made engine.
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Return to Zork
+ * - Leather Goddesses of Phobos 2
+ * - The Manhole
+ * - Rodney's Funscreen
*/
namespace Made {
@@ -97,6 +101,8 @@ public:
virtual bool hasFeature(EngineFeature f) const;
virtual void syncSoundSettings();
+ GUI::Debugger *getDebugger() { return _console; }
+
int getGameId() {
return _gameId;
}
@@ -109,6 +115,7 @@ public:
Common::Platform getPlatform() const;
private:
+ MadeConsole *_console;
public:
PmvPlayer *_pmvPlayer;
ResourceReader *_res;
diff --git a/engines/made/module.mk b/engines/made/module.mk
index 78c906b288..d8c0c9eeb5 100644
--- a/engines/made/module.mk
+++ b/engines/made/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/made
MODULE_OBJS := \
+ console.o \
database.o \
detection.o \
graphics.o \
diff --git a/engines/made/redreader.cpp b/engines/made/redreader.cpp
index 4ca8d27eab..3d36b69a28 100644
--- a/engines/made/redreader.cpp
+++ b/engines/made/redreader.cpp
@@ -24,11 +24,11 @@
*/
#include "made/redreader.h"
+#include "common/memstream.h"
namespace Made {
-
-Common::MemoryReadStream *RedReader::load(const char *redFilename, const char *filename) {
+Common::SeekableReadStream *RedReader::load(const char *redFilename, const char *filename) {
Common::File fd;
FileEntry fileEntry;
@@ -49,9 +49,9 @@ Common::MemoryReadStream *RedReader::load(const char *redFilename, const char *f
}
-Common::MemoryReadStream *RedReader::loadFromRed(const char *redFilename, const char *filename) {
+Common::SeekableReadStream *RedReader::loadFromRed(const char *redFilename, const char *filename) {
RedReader* red = new RedReader();
- Common::MemoryReadStream* stream = red->load(redFilename, filename);
+ Common::SeekableReadStream *stream = red->load(redFilename, filename);
delete red;
return stream;
}
diff --git a/engines/made/redreader.h b/engines/made/redreader.h
index b734ca02e1..a6e72c4e00 100644
--- a/engines/made/redreader.h
+++ b/engines/made/redreader.h
@@ -34,8 +34,8 @@ namespace Made {
class RedReader {
public:
- Common::MemoryReadStream *load(const char *redFilename, const char *filename);
- static Common::MemoryReadStream *loadFromRed(const char *redFilename, const char *filename);
+ Common::SeekableReadStream *load(const char *redFilename, const char *filename);
+ static Common::SeekableReadStream *loadFromRed(const char *redFilename, const char *filename);
private:
struct FileEntry {
uint32 compSize, origSize;
diff --git a/engines/made/resource.cpp b/engines/made/resource.cpp
index cdcb49f9f9..6140e7e0de 100644
--- a/engines/made/resource.cpp
+++ b/engines/made/resource.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/memstream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
@@ -295,7 +296,6 @@ void MenuResource::load(byte *source, int size) {
_strings.push_back(string);
debug(2, "%02d: %s\n", i, string);
}
- fflush(stdout);
delete sourceS;
}
diff --git a/engines/made/screen.cpp b/engines/made/screen.cpp
index 81c18ef64b..773e3d17ed 100644
--- a/engines/made/screen.cpp
+++ b/engines/made/screen.cpp
@@ -204,7 +204,7 @@ void Screen::drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 f
for (int16 yc = 0; yc < clipHeight; yc++) {
linePtr = source + sourceAdd;
for (int16 xc = 0; xc < clipWidth; xc++) {
- if (*linePtr && (_vm->getGameID() == GID_RTZ || (mask == 0 || maskp[xc] == 0))) {
+ if (*linePtr && (_vm->getGameID() == GID_RTZ || (mask == 0 || (maskp && maskp[xc] == 0)))) {
if (*linePtr)
dest[xc] = *linePtr;
}
diff --git a/engines/made/scriptfuncs.cpp b/engines/made/scriptfuncs.cpp
index 2f069e882e..1a17beb19b 100644
--- a/engines/made/scriptfuncs.cpp
+++ b/engines/made/scriptfuncs.cpp
@@ -26,6 +26,8 @@
#include "common/endian.h"
#include "common/util.h"
#include "common/events.h"
+
+#include "backends/audiocd/audiocd.h"
#include "graphics/cursorman.h"
#include "sound/softsynth/pcspk.h"
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 7519feaaa4..7b69a3fe43 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -231,6 +231,7 @@ private:
friend class Common::Singleton<SingletonBaseType>;
public:
+ GameDescriptor findGameOnePluginAtATime(const Common::String &gameName, const EnginePlugin **plugin = NULL) const;
GameDescriptor findGame(const Common::String &gameName, const EnginePlugin **plugin = NULL) const;
GameList detectGames(const Common::FSList &fslist) const;
const EnginePlugin::List &getPlugins() const;
diff --git a/engines/mohawk/bitmap.cpp b/engines/mohawk/bitmap.cpp
index b622b3efbf..73b21d14ec 100644
--- a/engines/mohawk/bitmap.cpp
+++ b/engines/mohawk/bitmap.cpp
@@ -28,6 +28,7 @@
#include "common/debug.h"
#include "common/util.h"
#include "common/endian.h"
+#include "common/memstream.h"
#include "common/system.h"
namespace Mohawk {
@@ -36,12 +37,28 @@ namespace Mohawk {
#define DRAW_COMPRESSION (_header.format & kDrawMASK)
MohawkBitmap::MohawkBitmap() {
+ static const PackFunction packTable[] = {
+ { kPackNone, "Raw", &MohawkBitmap::unpackRaw },
+ { kPackLZ, "LZ", &MohawkBitmap::unpackLZ },
+ { kPackRiven, "Riven", &MohawkBitmap::unpackRiven }
+ };
+
+ _packTable = packTable;
+ _packTableSize = ARRAYSIZE(packTable);
+
+ static const DrawFunction drawTable[] = {
+ { kDrawRaw, "Raw", &MohawkBitmap::drawRaw },
+ { kDrawRLE8, "RLE8", &MohawkBitmap::drawRLE8 }
+ };
+
+ _drawTable = drawTable;
+ _drawTableSize = ARRAYSIZE(drawTable);
}
MohawkBitmap::~MohawkBitmap() {
}
-ImageData *MohawkBitmap::decodeImage(Common::SeekableReadStream *stream) {
+MohawkSurface *MohawkBitmap::decodeImage(Common::SeekableReadStream *stream) {
_data = stream;
_header.colorTable.palette = NULL;
@@ -54,7 +71,7 @@ ImageData *MohawkBitmap::decodeImage(Common::SeekableReadStream *stream) {
debug (2, "Decoding Mohawk Bitmap (%dx%d, %dbpp, %s Packing + %s Drawing)", _header.width, _header.height, getBitsPerPixel(), getPackName(), getDrawName());
- if (getBitsPerPixel() != 8)
+ if (getBitsPerPixel() != 8 && getBitsPerPixel() != 24)
error ("Unhandled bpp %d", getBitsPerPixel());
// Read in the palette if it's here.
@@ -72,14 +89,20 @@ ImageData *MohawkBitmap::decodeImage(Common::SeekableReadStream *stream) {
}
}
- _surface = new Graphics::Surface();
- _surface->create(_header.width, _header.height, getBitsPerPixel() >> 3);
+ Graphics::Surface *surface = createSurface(_header.width, _header.height);
unpackImage();
- drawImage();
+ drawImage(surface);
delete _data;
- return new ImageData(_surface, _header.colorTable.palette);
+ return new MohawkSurface(surface, _header.colorTable.palette);
+}
+
+Graphics::Surface *MohawkBitmap::createSurface(uint16 width, uint16 height) {
+ Graphics::Surface *surface = new Graphics::Surface();
+ byte bytesPerPixel = (getBitsPerPixel() <= 8) ? 1 : g_system->getScreenFormat().bytesPerPixel;
+ surface->create(width, height, bytesPerPixel);
+ return surface;
}
byte MohawkBitmap::getBitsPerPixel() {
@@ -95,65 +118,46 @@ byte MohawkBitmap::getBitsPerPixel() {
case kBitsPerPixel24:
return 24;
default:
- error ("Unknown bits per pixel");
+ error("Unknown bits per pixel");
}
return 0;
}
-struct CompressionInfo {
- uint16 flag;
- const char *name;
- void (MohawkBitmap::*func)();
-};
-
-static const CompressionInfo packTable[] = {
- { kPackNone, "Raw", &MohawkBitmap::unpackRaw },
- { kPackLZ, "LZ", &MohawkBitmap::unpackLZ },
- { kPackLZ1, "LZ1", &MohawkBitmap::unpackLZ1 },
- { kPackRiven, "Riven", &MohawkBitmap::unpackRiven }
-};
-
const char *MohawkBitmap::getPackName() {
- for (uint32 i = 0; i < ARRAYSIZE(packTable); i++)
- if (PACK_COMPRESSION == packTable[i].flag)
- return packTable[i].name;
+ for (int i = 0; i < _packTableSize; i++)
+ if (PACK_COMPRESSION == _packTable[i].flag)
+ return _packTable[i].name;
return "Unknown";
}
void MohawkBitmap::unpackImage() {
- for (uint32 i = 0; i < ARRAYSIZE(packTable); i++)
- if (PACK_COMPRESSION == packTable[i].flag) {
- (this->*packTable[i].func)();
+ for (int i = 0; i < _packTableSize; i++)
+ if (PACK_COMPRESSION == _packTable[i].flag) {
+ (this->*_packTable[i].func)();
return;
}
- warning("Unknown Pack Compression");
+ error("Unknown Pack Compression");
}
-static const CompressionInfo drawTable[] = {
- { kDrawRaw, "Raw", &MohawkBitmap::drawRaw },
- { kDrawRLE8, "RLE8", &MohawkBitmap::drawRLE8 },
- { kDrawRLE, "RLE", &MohawkBitmap::drawRLE }
-};
-
const char *MohawkBitmap::getDrawName() {
- for (uint32 i = 0; i < ARRAYSIZE(drawTable); i++)
- if (DRAW_COMPRESSION == drawTable[i].flag)
- return drawTable[i].name;
+ for (int i = 0; i < _drawTableSize; i++)
+ if (DRAW_COMPRESSION == _drawTable[i].flag)
+ return _drawTable[i].name;
return "Unknown";
}
-void MohawkBitmap::drawImage() {
- for (uint32 i = 0; i < ARRAYSIZE(drawTable); i++)
- if (DRAW_COMPRESSION == drawTable[i].flag) {
- (this->*drawTable[i].func)();
+void MohawkBitmap::drawImage(Graphics::Surface *surface) {
+ for (int i = 0; i < _drawTableSize; i++)
+ if (DRAW_COMPRESSION == _drawTable[i].flag) {
+ (this->*_drawTable[i].func)(surface);
return;
}
- warning("Unknown Draw Compression");
+ error("Unknown Draw Compression");
}
//////////////////////////////////////////
@@ -266,14 +270,6 @@ void MohawkBitmap::unpackLZ() {
}
//////////////////////////////////////////
-// LZ Unpacker
-//////////////////////////////////////////
-
-void MohawkBitmap::unpackLZ1() {
- error("STUB: unpackLZ1()");
-}
-
-//////////////////////////////////////////
// Riven Unpacker
//////////////////////////////////////////
@@ -527,10 +523,28 @@ void MohawkBitmap::handleRivenSubcommandStream(byte count, byte *&dst) {
// Raw Drawer
//////////////////////////////////////////
-void MohawkBitmap::drawRaw() {
+void MohawkBitmap::drawRaw(Graphics::Surface *surface) {
+ assert(surface);
+
for (uint16 y = 0; y < _header.height; y++) {
- _data->read((byte *)_surface->pixels + y * _header.width, _header.width);
- _data->skip(_header.bytesPerRow - _header.width);
+ if (getBitsPerPixel() == 24) {
+ Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
+
+ for (uint16 x = 0; x < _header.width; x++) {
+ byte b = _data->readByte();
+ byte g = _data->readByte();
+ byte r = _data->readByte();
+ if (surface->bytesPerPixel == 2)
+ *((uint16 *)surface->getBasePtr(x, y)) = pixelFormat.RGBToColor(r, g, b);
+ else
+ *((uint32 *)surface->getBasePtr(x, y)) = pixelFormat.RGBToColor(r, g, b);
+ }
+
+ _data->skip(_header.bytesPerRow - _header.width * 3);
+ } else {
+ _data->read((byte *)surface->pixels + y * _header.width, _header.width);
+ _data->skip(_header.bytesPerRow - _header.width);
+ }
}
}
@@ -538,20 +552,18 @@ void MohawkBitmap::drawRaw() {
// RLE8 Drawer
//////////////////////////////////////////
-void MohawkBitmap::drawRLE8() {
+void MohawkBitmap::drawRLE8(Graphics::Surface *surface, bool isLE) {
// A very simple RLE8 scheme is used as a secondary compression on
// most images in non-Riven tBMP's.
+ assert(surface);
+
for (uint16 i = 0; i < _header.height; i++) {
- uint16 rowByteCount = _data->readUint16BE();
+ uint16 rowByteCount = isLE ? _data->readUint16LE() : _data->readUint16BE();
int32 startPos = _data->pos();
- byte *dst = (byte *)_surface->pixels + i * _header.width;
+ byte *dst = (byte *)surface->pixels + i * _header.width;
int16 remaining = _header.width;
- // HACK: It seems only the bottom 9 bits are valid for images
- // TODO: Verify if this is still needed after the buffer clearing fix.
- rowByteCount &= 0x1ff;
-
while (remaining > 0) {
byte code = _data->readByte();
uint16 runLen = (code & 0x7F) + 1;
@@ -576,18 +588,10 @@ void MohawkBitmap::drawRLE8() {
}
//////////////////////////////////////////
-// RLE Drawer
-//////////////////////////////////////////
-
-void MohawkBitmap::drawRLE() {
- warning("STUB: drawRLE()");
-}
-
-//////////////////////////////////////////
// Myst Bitmap Decoder
//////////////////////////////////////////
-ImageData* MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
+MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
uint32 uncompressedSize = stream->readUint32LE();
Common::SeekableReadStream* bmpStream = decompressLZ(stream, uncompressedSize);
delete stream;
@@ -642,12 +646,11 @@ ImageData* MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
bmpStream->seek(_header.imageOffset);
- Graphics::Surface *surface = new Graphics::Surface();
+ Graphics::Surface *surface = createSurface(_info.width, _info.height);
int srcPitch = _info.width * (_info.bitsPerPixel >> 3);
const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
if (_info.bitsPerPixel == 8) {
- surface->create(_info.width, _info.height, 1);
byte *dst = (byte *)surface->pixels;
for (uint32 i = 0; i < _info.height; i++) {
@@ -656,7 +659,6 @@ ImageData* MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
}
} else {
Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
- surface->create(_info.width, _info.height, pixelFormat.bytesPerPixel);
byte *dst = (byte *)surface->pixels + (surface->h - 1) * surface->pitch;
@@ -681,25 +683,79 @@ ImageData* MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
delete bmpStream;
- return new ImageData(surface, palData);
+ return new MohawkSurface(surface, palData);
}
-ImageData *OldMohawkBitmap::decodeImage(Common::SeekableReadStream *stream) {
+MohawkSurface *OldMohawkBitmap::decodeImage(Common::SeekableReadStream *stream) {
Common::SeekableSubReadStreamEndian *endianStream = (Common::SeekableSubReadStreamEndian *)stream;
- // The format part is just a guess at this point. Note that the width and height roles have been reversed!
-
- _header.height = endianStream->readUint16() & 0x3FF;
- _header.width = endianStream->readUint16() & 0x3FF;
- _header.bytesPerRow = endianStream->readUint16() & 0x3FE;
+ // 12 bytes header for the image
_header.format = endianStream->readUint16();
+ _header.bytesPerRow = endianStream->readUint16();
+ _header.width = endianStream->readUint16();
+ _header.height = endianStream->readUint16();
+ int offsetX = endianStream->readSint16();
+ int offsetY = endianStream->readSint16();
+
+ debug(7, "Decoding Old Mohawk Bitmap (%dx%d, %d bytesPerRow, %04x Format)", _header.width, _header.height, _header.bytesPerRow, _header.format);
+ debug(7, "Offset X = %d, Y = %d", offsetX, offsetY);
+
+ bool leRLE8 = false;
+
+ if ((_header.format & 0xf0) == kOldPackLZ) {
+ // 12 bytes header for the compressed data
+ uint32 uncompressedSize = endianStream->readUint32();
+ uint32 compressedSize = endianStream->readUint32();
+ uint16 posBits = endianStream->readUint16();
+ uint16 lengthBits = endianStream->readUint16();
- debug(2, "Decoding Old Mohawk Bitmap (%dx%d, %04x Format)", _header.width, _header.height, _header.format);
+ if (compressedSize != (uint32)endianStream->size() - 24)
+ error("More bytes (%d) remaining in stream than header says there should be (%d)", endianStream->size() - 24, compressedSize);
- warning("Unhandled old Mohawk Bitmap decoding");
+ // These two errors are really just sanity checks and should never go off
+ if (posBits != POS_BITS)
+ error("Position bits modified to %d", posBits);
+ if (lengthBits != LEN_BITS)
+ error("Length bits modified to %d", lengthBits);
+ _data = decompressLZ(stream, uncompressedSize);
+
+ if (endianStream->pos() != endianStream->size())
+ error("OldMohawkBitmap decompression failed");
+ } else {
+ if ((_header.format & 0xf0) != 0)
+ error("Tried to use unknown OldMohawkBitmap compression (format %02x)", _header.format & 0xf0);
+
+ // This is so nasty on so many levels. The original Windows LZ decompressor for the
+ // Living Books v1 games had knowledge of the underlying RLE8 data. While going
+ // through the LZ data, it would byte swap the RLE8 length fields to make them LE.
+ // This is an extremely vile thing and there's no way in hell that I'm doing
+ // anything similar. When no LZ compression is used, the underlying RLE8 fields
+ // are LE, so we need to set a swap in this condition for LE vs. BE in the RLE8
+ // decoder. *sigh*
+
+ if (!endianStream->isBE())
+ leRLE8 = true;
+
+ _data = stream;
+ stream = NULL;
+ }
+
+ Graphics::Surface *surface = createSurface(_header.width, _header.height);
+
+ if ((_header.format & 0xf00) == kOldDrawRLE8)
+ drawRLE8(surface, leRLE8);
+ else
+ drawRaw(surface);
+
+ delete _data;
delete stream;
- return new ImageData(NULL, NULL);
+
+ MohawkSurface *mhkSurface = new MohawkSurface(surface);
+ mhkSurface->setOffsetX(offsetX);
+ mhkSurface->setOffsetY(offsetY);
+
+ return mhkSurface;
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/bitmap.h b/engines/mohawk/bitmap.h
index f1fde92f33..8f3f6af436 100644
--- a/engines/mohawk/bitmap.h
+++ b/engines/mohawk/bitmap.h
@@ -34,7 +34,7 @@
namespace Mohawk {
-class ImageData;
+class MohawkSurface;
enum BitmapFormat {
kBitsPerPixel1 = 0x0000,
@@ -60,6 +60,11 @@ enum BitmapFormat {
kFlag24_MAC = 0x1000 // 24 bit pixel data has been converted to MAC 32 bit format
};
+enum OldBitmapFormat {
+ kOldPackLZ = 0x0020,
+ kOldDrawRLE8 = 0x0100
+};
+
struct BitmapHeader {
uint16 width;
uint16 height;
@@ -79,34 +84,57 @@ public:
MohawkBitmap();
virtual ~MohawkBitmap();
- virtual ImageData *decodeImage(Common::SeekableReadStream *stream);
-
- // Unpack Functions
- void unpackRaw();
- void unpackLZ();
- void unpackLZ1();
- void unpackRiven();
-
- // Draw Functions
- void drawRaw();
- void drawRLE8();
- void drawRLE();
+ virtual MohawkSurface *decodeImage(Common::SeekableReadStream *stream);
protected:
BitmapHeader _header;
- byte getBitsPerPixel();
+ virtual byte getBitsPerPixel();
// The actual LZ decoder
static Common::SeekableReadStream *decompressLZ(Common::SeekableReadStream *stream, uint32 uncompressedSize);
-private:
+ // The current data stream
Common::SeekableReadStream *_data;
- Graphics::Surface *_surface;
+ // Create the output surface
+ Graphics::Surface *createSurface(uint16 width, uint16 height);
+
+ // Draw Functions
+ void drawRLE8(Graphics::Surface *surface, bool isLE);
+ void drawRaw(Graphics::Surface *surface);
+ void drawRLE8(Graphics::Surface *surface) { return drawRLE8(surface, false); }
+
+private:
+ // Unpack Functions
+ void unpackRaw();
+ void unpackLZ();
+ void unpackRiven();
+
+ // An unpacker
+ struct PackFunction {
+ uint16 flag;
+ const char *name;
+ void (MohawkBitmap::*func)();
+ };
+
+ // A drawer
+ struct DrawFunction {
+ uint16 flag;
+ const char *name;
+ void (MohawkBitmap::*func)(Graphics::Surface *surface);
+ };
+
+ // Unpack/Draw maps
+ const PackFunction *_packTable;
+ int _packTableSize;
+ const DrawFunction *_drawTable;
+ int _drawTableSize;
+
+ // Unpack/Draw helpers
const char *getPackName();
void unpackImage();
const char *getDrawName();
- void drawImage();
+ void drawImage(Graphics::Surface *surface);
// Riven Decoding
void handleRivenSubcommandStream(byte count, byte *&dst);
@@ -120,7 +148,10 @@ public:
MystBitmap() : MohawkBitmap() {}
~MystBitmap() {}
- ImageData *decodeImage(Common::SeekableReadStream *stream);
+ MohawkSurface *decodeImage(Common::SeekableReadStream *stream);
+
+protected:
+ byte getBitsPerPixel() { return _info.bitsPerPixel; }
private:
struct BitmapHeader {
@@ -151,7 +182,10 @@ public:
OldMohawkBitmap() : MohawkBitmap() {}
~OldMohawkBitmap() {}
- ImageData *decodeImage(Common::SeekableReadStream *stream);
+ MohawkSurface *decodeImage(Common::SeekableReadStream *stream);
+
+protected:
+ byte getBitsPerPixel() { return 8; }
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 3eed08e3c1..e25ff030d0 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -502,7 +502,7 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
_vm->changeToStack(newStack);
// Load in Variable Names
- Common::SeekableReadStream *nameStream = _vm->getRawData(ID_NAME, VariableNames);
+ Common::SeekableReadStream *nameStream = _vm->getResource(ID_NAME, VariableNames);
Common::StringArray varNames;
uint16 namesCount = nameStream->readUint16BE();
@@ -523,7 +523,7 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
delete nameStream;
// Load in External Command Names
- nameStream = _vm->getRawData(ID_NAME, ExternalCommandNames);
+ nameStream = _vm->getResource(ID_NAME, ExternalCommandNames);
Common::StringArray xNames;
namesCount = nameStream->readUint16BE();
@@ -545,9 +545,15 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
// Get CARD/HSPT data and dump their scripts
if (!scumm_stricmp(argv[2], "CARD")) {
- printf ("\n\nDumping scripts for %s\'s card %d!\n", argv[1], (uint16)atoi(argv[3]));
- printf ("==================================\n\n");
- Common::SeekableReadStream *cardStream = _vm->getRawData(MKID_BE('CARD'), (uint16)atoi(argv[3]));
+ // Use debugN to print these because the scripts can get very large and would
+ // really be useless if the the text console is not used. A DumpFile could also
+ // theoretically be used, but I (clone2727) typically use this dynamically and
+ // don't want countless files laying around without game context. If one would
+ // want a file of a script they could just redirect stdout to a file or use
+ // deriven.
+ debugN("\n\nDumping scripts for %s\'s card %d!\n", argv[1], (uint16)atoi(argv[3]));
+ debugN("==================================\n\n");
+ Common::SeekableReadStream *cardStream = _vm->getResource(MKID_BE('CARD'), (uint16)atoi(argv[3]));
cardStream->seek(4);
RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream, false);
for (uint32 i = 0; i < scriptList.size(); i++) {
@@ -556,15 +562,16 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
}
delete cardStream;
} else if (!scumm_stricmp(argv[2], "HSPT")) {
- printf ("\n\nDumping scripts for %s\'s card %d hotspots!\n", argv[1], (uint16)atoi(argv[3]));
- printf ("===========================================\n\n");
+ // See above for why this is printed via debugN
+ debugN("\n\nDumping scripts for %s\'s card %d hotspots!\n", argv[1], (uint16)atoi(argv[3]));
+ debugN("===========================================\n\n");
- Common::SeekableReadStream *hsptStream = _vm->getRawData(MKID_BE('HSPT'), (uint16)atoi(argv[3]));
+ Common::SeekableReadStream *hsptStream = _vm->getResource(MKID_BE('HSPT'), (uint16)atoi(argv[3]));
uint16 hotspotCount = hsptStream->readUint16BE();
for (uint16 i = 0; i < hotspotCount; i++) {
- printf ("Hotspot %d:\n", i);
+ debugN("Hotspot %d:\n", i);
hsptStream->seek(22, SEEK_CUR); // Skip non-script related stuff
RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream, false);
for (uint32 j = 0; j < scriptList.size(); j++) {
@@ -578,7 +585,8 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
DebugPrintf("%s doesn't have any scripts!\n", argv[2]);
}
- printf("\n\n");
+ // See above for why this is printed via debugN
+ debugN("\n\n");
_vm->changeToStack(oldStack);
@@ -660,13 +668,11 @@ void LivingBooksConsole::postEnter() {
bool LivingBooksConsole::Cmd_PlaySound(int argc, const char **argv) {
if (argc == 1) {
DebugPrintf("Usage: playSound <value>\n");
-
return true;
}
_vm->_sound->stopSound();
_vm->_sound->playSound((uint16)atoi(argv[1]));
-
return false;
}
@@ -674,7 +680,6 @@ bool LivingBooksConsole::Cmd_StopSound(int argc, const char **argv) {
DebugPrintf("Stopping Sound\n");
_vm->_sound->stopSound();
-
return true;
}
@@ -684,11 +689,8 @@ bool LivingBooksConsole::Cmd_DrawImage(int argc, const char **argv) {
return true;
}
- if (_vm->getGameType() == GType_LIVINGBOOKSV1)
- DebugPrintf("This isn't supported in the old Living Books games (yet)!\n");
-
_vm->_gfx->copyImageToScreen((uint16)atoi(argv[1]));
- return _vm->getGameType() != GType_LIVINGBOOKSV1;
+ return false;
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp
new file mode 100644
index 0000000000..3b937385f9
--- /dev/null
+++ b/engines/mohawk/cursors.cpp
@@ -0,0 +1,195 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "mohawk/bitmap.h"
+#include "mohawk/cursors.h"
+#include "mohawk/resource.h"
+#include "mohawk/graphics.h"
+#include "mohawk/myst.h"
+#include "mohawk/riven_cursors.h"
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+
+namespace Mohawk {
+
+void CursorManager::showCursor() {
+ CursorMan.showMouse(true);
+}
+
+void CursorManager::hideCursor() {
+ CursorMan.showMouse(false);
+}
+
+MystCursorManager::MystCursorManager(MohawkEngine_Myst *vm) : _vm(vm) {
+ _bmpDecoder = new MystBitmap();
+}
+
+MystCursorManager::~MystCursorManager() {
+ delete _bmpDecoder;
+}
+
+void MystCursorManager::showCursor() {
+ CursorMan.showMouse(true);
+ _vm->_needsUpdate = true;
+}
+
+void MystCursorManager::hideCursor() {
+ CursorMan.showMouse(false);
+ _vm->_needsUpdate = true;
+}
+
+void MystCursorManager::setCursor(uint16 id) {
+ // Both Myst and Myst ME use the "MystBitmap" format for cursor images.
+ MohawkSurface *mhkSurface = _bmpDecoder->decodeImage(_vm->getResource(ID_WDIB, id));
+ Graphics::Surface *surface = mhkSurface->getSurface();
+ Common::SeekableReadStream *clrcStream = _vm->getResource(ID_CLRC, id);
+ uint16 hotspotX = clrcStream->readUint16LE();
+ uint16 hotspotY = clrcStream->readUint16LE();
+ delete clrcStream;
+
+ // Myst ME stores some cursors as 24bpp images instead of 8bpp
+ if (surface->bytesPerPixel == 1) {
+ CursorMan.replaceCursor((byte *)surface->pixels, surface->w, surface->h, hotspotX, hotspotY, 0);
+ CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256);
+ } else {
+ Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
+ CursorMan.replaceCursor((byte *)surface->pixels, surface->w, surface->h, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), 1, &pixelFormat);
+ }
+
+ _vm->_needsUpdate = true;
+ delete mhkSurface;
+}
+
+void RivenCursorManager::setCursor(uint16 id) {
+ // All of Riven's cursors are hardcoded. See riven_cursors.h for these definitions.
+
+ switch (id) {
+ case 1002:
+ // Zip Mode
+ CursorMan.replaceCursor(s_zipModeCursor, 16, 16, 8, 8, 0);
+ CursorMan.replaceCursorPalette(s_zipModeCursorPalette, 1, ARRAYSIZE(s_zipModeCursorPalette) / 4);
+ break;
+ case 2003:
+ // Hand Over Object
+ CursorMan.replaceCursor(s_objectHandCursor, 16, 16, 8, 8, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 2004:
+ // Grabbing/Using Object
+ CursorMan.replaceCursor(s_grabbingHandCursor, 13, 13, 6, 6, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3000:
+ // Standard Hand
+ CursorMan.replaceCursor(s_standardHandCursor, 15, 16, 6, 0, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3001:
+ // Pointing Left
+ CursorMan.replaceCursor(s_pointingLeftCursor, 15, 13, 0, 3, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3002:
+ // Pointing Right
+ CursorMan.replaceCursor(s_pointingRightCursor, 15, 13, 14, 3, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3003:
+ // Pointing Down (Palm Up)
+ CursorMan.replaceCursor(s_pointingDownCursorPalmUp, 13, 16, 3, 15, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3004:
+ // Pointing Up (Palm Up)
+ CursorMan.replaceCursor(s_pointingUpCursorPalmUp, 13, 16, 3, 0, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3005:
+ // Pointing Left (Curved)
+ CursorMan.replaceCursor(s_pointingLeftCursorBent, 15, 13, 0, 5, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3006:
+ // Pointing Right (Curved)
+ CursorMan.replaceCursor(s_pointingRightCursorBent, 15, 13, 14, 5, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 3007:
+ // Pointing Down (Palm Down)
+ CursorMan.replaceCursor(s_pointingDownCursorPalmDown, 15, 16, 7, 15, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
+ break;
+ case 4001:
+ // Red Marble
+ CursorMan.replaceCursor(s_redMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_redMarbleCursorPalette, 1, ARRAYSIZE(s_redMarbleCursorPalette) / 4);
+ break;
+ case 4002:
+ // Orange Marble
+ CursorMan.replaceCursor(s_orangeMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_orangeMarbleCursorPalette, 1, ARRAYSIZE(s_orangeMarbleCursorPalette) / 4);
+ break;
+ case 4003:
+ // Yellow Marble
+ CursorMan.replaceCursor(s_yellowMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_yellowMarbleCursorPalette, 1, ARRAYSIZE(s_yellowMarbleCursorPalette) / 4);
+ break;
+ case 4004:
+ // Green Marble
+ CursorMan.replaceCursor(s_greenMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_greenMarbleCursorPalette, 1, ARRAYSIZE(s_greenMarbleCursorPalette) / 4);
+ break;
+ case 4005:
+ // Blue Marble
+ CursorMan.replaceCursor(s_blueMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_blueMarbleCursorPalette, 1, ARRAYSIZE(s_blueMarbleCursorPalette) / 4);
+ break;
+ case 4006:
+ // Violet Marble
+ CursorMan.replaceCursor(s_violetMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_violetMarbleCursorPalette, 1, ARRAYSIZE(s_violetMarbleCursorPalette) / 4);
+ break;
+ case 5000:
+ // Pellet
+ CursorMan.replaceCursor(s_pelletCursor, 8, 8, 4, 4, 0);
+ CursorMan.replaceCursorPalette(s_pelletCursorPalette, 1, ARRAYSIZE(s_pelletCursorPalette) / 4);
+ break;
+ case 9000:
+ // Hide Cursor
+ CursorMan.showMouse(false);
+ break;
+ default:
+ error("Cursor %d does not exist!", id);
+ }
+
+ if (id != 9000) // Show Cursor
+ CursorMan.showMouse(true);
+
+ // Should help in cases where we need to hide the cursor immediately.
+ g_system->updateScreen();
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/cursors.h b/engines/mohawk/cursors.h
new file mode 100644
index 0000000000..90858a2421
--- /dev/null
+++ b/engines/mohawk/cursors.h
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef MOHAWK_CURSORS_H
+#define MOHAWK_CURSORS_H
+
+#include "common/scummsys.h"
+
+namespace Mohawk {
+
+// 803-805 are animated, one large bmp which is in chunks - these are NEVER USED
+// Other cursors (200, 300, 400, 500, 600, 700) are not the same in each stack
+enum {
+ kDefaultMystCursor = 100, // The default hand
+ kWhitePageCursor = 800, // Holding a white page
+ kRedPageCursor = 801, // Holding a red page
+ kBluePageCursor = 802, // Holding a blue page
+ // kDroppingWhitePageAnimCursor = 803,
+ // kDroppingRedPageAnimCursor = 804,
+ // kDroppingBluePageAnimCursor = 805,
+ kNewMatchCursor = 900, // Match that has not yet been lit
+ kLitMatchCursor = 901, // Match that's burning
+ kDeadMatchCursor = 902, // Match that's been extinguished
+ kKeyCursor = 903, // Key in Lighthouse in Stoneship
+ kRotateClockwiseCursor = 904, // Rotate gear clockwise (boiler on Myst)
+ kRotateCounterClockwiseCursor = 905, // Rotate gear counter clockwise (boiler on Myst)
+ kMystZipModeCursor = 999 // Zip Mode cursor
+};
+
+enum {
+ kRivenOpenHandCursor = 2003,
+ kRivenClosedHandCursor = 2004,
+ kRivenMainCursor = 3000,
+ kRivenPelletCursor = 5000,
+ kRivenHideCursor = 9000
+};
+
+class MohawkEngine_Myst;
+class MystBitmap;
+
+class CursorManager {
+public:
+ CursorManager() {}
+ virtual ~CursorManager() {}
+
+ virtual void showCursor();
+ virtual void hideCursor();
+ virtual void setCursor(uint16 id) = 0;
+};
+
+class MystCursorManager : public CursorManager {
+public:
+ MystCursorManager(MohawkEngine_Myst *vm);
+ ~MystCursorManager();
+
+ void showCursor();
+ void hideCursor();
+ void setCursor(uint16 id);
+
+private:
+ MohawkEngine_Myst *_vm;
+ MystBitmap *_bmpDecoder;
+};
+
+class RivenCursorManager : public CursorManager {
+public:
+ RivenCursorManager() {}
+ ~RivenCursorManager() {}
+
+ void setCursor(uint16 id);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index f04338239f..417560f67f 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -44,7 +44,7 @@ struct MohawkGameDescription {
uint8 gameType;
uint32 features;
- uint16 version;
+ const char *appName;
};
const char* MohawkEngine::getGameId() const {
@@ -59,8 +59,8 @@ Common::Platform MohawkEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
-uint16 MohawkEngine::getVersion() const {
- return _gameDescription->version;
+const char *MohawkEngine::getAppName() const {
+ return _gameDescription->appName;
}
uint8 MohawkEngine::getGameType() const {
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
index 7470dbf1dd..fb086d5d93 100644
--- a/engines/mohawk/detection_tables.h
+++ b/engines/mohawk/detection_tables.h
@@ -490,7 +490,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_JAMESMATH,
GF_HASMIDI,
- 1
+ 0
},
{
@@ -520,7 +520,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV3,
0,
- 0
+ "GREEN.EXE"
},
// 32-bit version of the previous entry
@@ -536,7 +536,22 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV3,
0,
- 0
+ "GREEN32.EXE"
+ },
+
+ {
+ {
+ "greeneggs",
+ "",
+ AD_ENTRY1("BookOutline", "5500fa72a6d112b4b3d3573b26a31820"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ "Green Eggs and Ham"
},
{
@@ -599,7 +614,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "TORTOISE.EXE"
},
{
@@ -614,7 +629,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "TORTOISE.EXE"
},
{
@@ -629,7 +644,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Living Books Player"
},
{
@@ -644,7 +659,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
0,
- 0
+ "ARTHUR.EXE" // FIXME: Check this (ST?)
},
{
@@ -659,7 +674,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "ARTHUR.EXE"
},
{
@@ -674,7 +689,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "ARTHUR.EXE"
},
{
@@ -689,7 +704,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Arthur's Teacher Trouble"
},
{
@@ -704,7 +719,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Living Books Player"
},
{
@@ -719,7 +734,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "GRANDMA.EXE"
},
{
@@ -734,7 +749,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "GRANDMA.EXE"
},
{
@@ -749,7 +764,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Just Grandma and Me"
},
{
@@ -764,7 +779,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Living Books Player"
},
{
@@ -779,7 +794,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "RUFF.EXE"
},
{
@@ -794,7 +809,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Living Books Player"
},
{
@@ -809,7 +824,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "NEWKID.EXE"
},
{
@@ -824,7 +839,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "NEWKID.EXE"
},
{
@@ -839,7 +854,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Living Books Player"
},
{
@@ -854,7 +869,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV3,
0,
- 0
+ 0 // FIXME: ST?
},
// 32-bit version of the previous entry
@@ -870,7 +885,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV3,
0,
- 0
+ 0 // FIXME: ST?
},
{
@@ -885,7 +900,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "BIRTHDAY.EXE"
},
{
@@ -900,7 +915,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
GF_DEMO,
- 0
+ "Living Books Player"
},
{
@@ -915,7 +930,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
0,
- 0
+ "MONSTER.EXE"
},
{
@@ -930,7 +945,7 @@ static const MohawkGameDescription gameDescriptions[] = {
},
GType_LIVINGBOOKSV1,
0,
- 0
+ "Little Monster at School"
},
#endif
diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index a7369b4825..a4ac2da66c 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -28,20 +28,20 @@
#include "mohawk/riven.h"
#include "mohawk/dialogs.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
#include "common/savefile.h"
#include "common/translation.h"
namespace Mohawk {
// This used to have GUI::Dialog("MohawkDummyDialog"), but that doesn't work with the gui branch merge :P (Sorry, Tanoku!)
-InfoDialog::InfoDialog(MohawkEngine *vm, Common::String message) : _vm(vm), GUI::Dialog(0, 0, 1, 1), _message(message) {
+InfoDialog::InfoDialog(MohawkEngine *vm, const Common::String &message) : _vm(vm), GUI::Dialog(0, 0, 1, 1), _message(message) {
_backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
_text = new GUI::StaticTextWidget(this, 4, 4, 10, 10, _message, Graphics::kTextAlignCenter);
}
-void InfoDialog::setInfoText(Common::String message) {
+void InfoDialog::setInfoText(const Common::String &message) {
_message = message;
_text->setLabel(_message);
}
@@ -61,7 +61,7 @@ void InfoDialog::reflowLayout() {
_text->setSize(_w - 8, _h);
}
-PauseDialog::PauseDialog(MohawkEngine *vm, Common::String message) : InfoDialog(vm, message) {
+PauseDialog::PauseDialog(MohawkEngine *vm, const Common::String &message) : InfoDialog(vm, message) {
}
void PauseDialog::handleKeyDown(Common::KeyState state) {
diff --git a/engines/mohawk/dialogs.h b/engines/mohawk/dialogs.h
index 74e8f50b3d..d4e3a3331b 100644
--- a/engines/mohawk/dialogs.h
+++ b/engines/mohawk/dialogs.h
@@ -33,7 +33,7 @@
#include "gui/dialog.h"
#include "gui/options.h"
#include "gui/widget.h"
-#include "gui/ListWidget.h"
+#include "gui/widgets/list.h"
namespace Mohawk {
@@ -48,9 +48,9 @@ protected:
GUI::StaticTextWidget *_text;
public:
- InfoDialog(MohawkEngine *vm, Common::String message);
+ InfoDialog(MohawkEngine *vm, const Common::String &message);
- void setInfoText(Common::String message);
+ void setInfoText(const Common::String &message);
virtual void handleMouseDown(int x, int y, int button, int clickCount) {
setResult(0);
@@ -67,7 +67,7 @@ public:
class PauseDialog : public InfoDialog {
public:
- PauseDialog(MohawkEngine* vm, Common::String message);
+ PauseDialog(MohawkEngine* vm, const Common::String &message);
virtual void handleKeyDown(Common::KeyState state);
};
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index f472a9d721..65eebf7134 100644
--- a/engines/mohawk/graphics.cpp
+++ b/engines/mohawk/graphics.cpp
@@ -27,43 +27,94 @@
#include "mohawk/graphics.h"
#include "mohawk/myst.h"
#include "mohawk/riven.h"
-#include "mohawk/riven_cursors.h"
#include "engines/util.h"
-#include "graphics/cursorman.h"
#include "graphics/primitives.h"
#include "gui/message.h"
namespace Mohawk {
-Graphics::Surface *ImageData::getSurface() {
+MohawkSurface::MohawkSurface() : _surface(0), _palette(0) {
+ _offsetX = 0;
+ _offsetY = 0;
+}
+
+MohawkSurface::MohawkSurface(Graphics::Surface *surface, byte *palette, int offsetX, int offsetY) : _palette(palette), _offsetX(offsetX), _offsetY(offsetY) {
+ assert(surface);
+
+ _surface = surface;
+}
+
+MohawkSurface::~MohawkSurface() {
+ free(_palette);
+
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+}
+
+void MohawkSurface::convertToTrueColor() {
+ assert(_surface);
+
+ if (_surface->bytesPerPixel > 1)
+ return;
+
+ assert(_palette);
+
Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
Graphics::Surface *surface = new Graphics::Surface();
surface->create(_surface->w, _surface->h, pixelFormat.bytesPerPixel);
- if (_surface->bytesPerPixel == 1) {
- assert(_palette);
-
- for (uint16 i = 0; i < _surface->h; i++) {
- for (uint16 j = 0; j < _surface->w; j++) {
- byte palIndex = *((byte *)_surface->pixels + i * _surface->pitch + j);
- byte r = _palette[palIndex * 4];
- byte g = _palette[palIndex * 4 + 1];
- byte b = _palette[palIndex * 4 + 2];
- if (pixelFormat.bytesPerPixel == 2)
- *((uint16 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
- else
- *((uint32 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
- }
+ for (uint16 i = 0; i < _surface->h; i++) {
+ for (uint16 j = 0; j < _surface->w; j++) {
+ byte palIndex = *((byte *)_surface->pixels + i * _surface->pitch + j);
+ byte r = _palette[palIndex * 4];
+ byte g = _palette[palIndex * 4 + 1];
+ byte b = _palette[palIndex * 4 + 2];
+ if (pixelFormat.bytesPerPixel == 2)
+ *((uint16 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
+ else
+ *((uint32 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
}
- } else
- memcpy(surface->pixels, _surface->pixels, _surface->w * _surface->h * _surface->bytesPerPixel);
+ }
- return surface;
+ // Free everything and set the new surface as the converted surface
+ _surface->free();
+ delete _surface;
+ free(_palette);
+ _palette = 0;
+ _surface = surface;
+}
+
+GraphicsManager::GraphicsManager() {
+}
+
+GraphicsManager::~GraphicsManager() {
+ clearCache();
+}
+
+void GraphicsManager::clearCache() {
+ for (Common::HashMap<uint16, MohawkSurface*>::iterator it = _cache.begin(); it != _cache.end(); it++)
+ delete it->_value;
+
+ _cache.clear();
}
-MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : _vm(vm) {
+MohawkSurface *GraphicsManager::findImage(uint16 id) {
+ if (!_cache.contains(id))
+ _cache[id] = decodeImage(id);
+
+ // TODO: Probably would be nice to limit the size of the cache
+ // Currently, this can't get large because it is freed on every
+ // card/stack change in Myst/Riven so I'm not worried about it.
+ // Doesn't mean this shouldn't be done in the future.
+
+ return _cache[id];
+}
+
+MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
_bmpDecoder = new MystBitmap();
// The original version of Myst could run in 8bpp color too.
@@ -86,12 +137,21 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : _vm(vm) {
}
_pictureFile.entries = NULL;
+
+ // Initialize our buffer
+ _mainScreen = new Graphics::Surface();
+ _mainScreen->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat.bytesPerPixel);
+ _dirtyScreen = false;
}
MystGraphics::~MystGraphics() {
delete _bmpDecoder;
delete _jpegDecoder;
delete _pictDecoder;
+ delete[] _pictureFile.entries;
+
+ _mainScreen->free();
+ delete _mainScreen;
}
static const char* picFileNames[] = {
@@ -133,16 +193,8 @@ void MystGraphics::loadExternalPictureFile(uint16 stack) {
}
}
-void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) {
- // Clip the destination rect to the screen
- if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight())
- dest.debugPrint(4, "Clipping destination rect to the screen:");
-
- dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth());
- dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight());
-
- Graphics::Surface *surface = NULL;
-
+MohawkSurface *MystGraphics::decodeImage(uint16 id) {
+ MohawkSurface *mhkSurface = 0;
// Myst ME uses JPEG/PICT images instead of compressed Windows Bitmaps for room images,
// though there are a few weird ones that use that format. For further nonsense with images,
@@ -150,13 +202,14 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
// going to check for a PICT resource.
if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) {
for (uint32 i = 0; i < _pictureFile.pictureCount; i++)
- if (_pictureFile.entries[i].id == image) {
+ if (_pictureFile.entries[i].id == id) {
if (_pictureFile.entries[i].type == 0) {
- Graphics::Surface *jpegSurface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size));
- surface->copyFrom(*jpegSurface);
- } else if (_pictureFile.entries[i].type == 1)
- surface = _pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size));
- else
+ Graphics::Surface *surface = new Graphics::Surface();
+ surface->copyFrom(*_jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)));
+ mhkSurface = new MohawkSurface(surface);
+ } else if (_pictureFile.entries[i].type == 1) {
+ mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)));
+ } else
error ("Unknown Picture File type %d", _pictureFile.entries[i].type);
break;
}
@@ -167,14 +220,14 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
// 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.
- if (!surface) {
+ if (!mhkSurface) {
bool isPict = false;
Common::SeekableReadStream *dataStream = NULL;
- if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, image)) {
+ if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) {
// The PICT resource exists. However, it could still contain a MystBitmap
// instead of a PICT image...
- dataStream = _vm->getRawData(ID_PICT, image);
+ dataStream = _vm->getResource(ID_PICT, id);
// Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap
// would be compressed, there's no way to detect for the BM without a hack.
@@ -183,17 +236,30 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
isPict = (dataStream->readUint32BE() == 0x001102FF);
dataStream->seek(0);
} else // No PICT, so the WDIB must exist. Let's go grab it.
- dataStream = _vm->getRawData(ID_WDIB, image);
+ dataStream = _vm->getResource(ID_WDIB, id);
if (isPict)
- surface = _pictDecoder->decodeImage(dataStream);
+ mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(dataStream));
else {
- ImageData *imageData = _bmpDecoder->decodeImage(dataStream);
- surface = imageData->getSurface();
- delete imageData;
+ mhkSurface = _bmpDecoder->decodeImage(dataStream);
+ mhkSurface->convertToTrueColor();
}
}
+ assert(mhkSurface);
+ return mhkSurface;
+}
+
+void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) {
+ // Clip the destination rect to the screen
+ if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight())
+ dest.debugPrint(4, "Clipping destination rect to the screen:");
+
+ dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth());
+ dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight());
+
+ Graphics::Surface *surface = findImage(image)->getSurface();
+
debug(3, "Image Blit:");
debug(3, "src.x: %d", src.left);
debug(3, "src.y: %d", src.top);
@@ -202,48 +268,30 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
debug(3, "width: %d", src.width());
debug(3, "height: %d", src.height());
- if (surface) {
- uint16 width = MIN<int>(surface->w, dest.width());
- uint16 height = MIN<int>(surface->h, dest.height());
- _vm->_system->copyRectToScreen((byte *)surface->getBasePtr(src.left, src.top), surface->pitch, dest.left, dest.top, width, height);
- surface->free();
- delete surface;
- }
+ uint16 width = MIN<int>(surface->w, dest.width());
+ uint16 height = MIN<int>(surface->h, dest.height());
- // FIXME: Remove this and update only at certain points
- _vm->_system->updateScreen();
-}
+ // Convert from bitmap coordinates to surface coordinates
+ uint16 top = surface->h - src.top - height;
-void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) {
- copyImageSectionToScreen(image, Common::Rect(0, 0, 544, 333), dest);
-}
+ for (uint16 i = 0; i < height; i++)
+ memcpy(_mainScreen->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->bytesPerPixel);
-void MystGraphics::showCursor(void) {
- CursorMan.showMouse(true);
- _vm->_needsUpdate = true;
+ // Mark the screen as dirty
+ _dirtyScreen = true;
}
-void MystGraphics::hideCursor(void) {
- CursorMan.showMouse(false);
- _vm->_needsUpdate = true;
+void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) {
+ copyImageSectionToScreen(image, Common::Rect(0, 0, 544, 333), dest);
}
-void MystGraphics::changeCursor(uint16 cursor) {
- // Both Myst and Myst ME use the "MystBitmap" format for cursor images.
- ImageData *data = _bmpDecoder->decodeImage(_vm->getRawData(ID_WDIB, cursor));
- Common::SeekableReadStream *clrcStream = _vm->getRawData(ID_CLRC, cursor);
- uint16 hotspotX = clrcStream->readUint16LE();
- uint16 hotspotY = clrcStream->readUint16LE();
- delete clrcStream;
-
- // Myst ME stores some cursors as 24bpp images instead of 8bpp
- if (data->_surface->bytesPerPixel == 1) {
- CursorMan.replaceCursor((byte *)data->_surface->pixels, data->_surface->w, data->_surface->h, hotspotX, hotspotY, 0);
- CursorMan.replaceCursorPalette(data->_palette, 0, 256);
- } else
- CursorMan.replaceCursor((byte *)data->_surface->pixels, data->_surface->w, data->_surface->h, hotspotX, hotspotY, _pixelFormat.RGBToColor(255, 255, 255), 1, &_pixelFormat);
-
- _vm->_needsUpdate = true;
+void MystGraphics::updateScreen() {
+ if (_dirtyScreen) {
+ // Only copy the buffer to the screen if it's dirty
+ _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h);
+ _vm->_system->updateScreen();
+ _dirtyScreen = false;
+ }
}
void MystGraphics::drawRect(Common::Rect rect, bool active) {
@@ -261,7 +309,7 @@ void MystGraphics::drawRect(Common::Rect rect, bool active) {
_vm->_system->unlockScreen();
}
-RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : _vm(vm) {
+RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) {
_bitmapDecoder = new MohawkBitmap();
// Give me the best you've got!
@@ -288,11 +336,14 @@ RivenGraphics::~RivenGraphics() {
delete _bitmapDecoder;
}
+MohawkSurface *RivenGraphics::decodeImage(uint16 id) {
+ MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getResource(ID_TBMP, id));
+ surface->convertToTrueColor();
+ return surface;
+}
+
void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom) {
- // First, decode the image and get the high color surface
- ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getRawData(ID_TBMP, image));
- Graphics::Surface *surface = imageData->getSurface();
- delete imageData;
+ Graphics::Surface *surface = findImage(image)->getSurface();
// Clip the width to fit on the screen. Fixes some images.
if (left + surface->w > 608)
@@ -301,14 +352,11 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
for (uint16 i = 0; i < surface->h; i++)
memcpy(_mainScreen->getBasePtr(left, i + top), surface->getBasePtr(0, i), surface->w * surface->bytesPerPixel);
- surface->free();
- delete surface;
-
_dirtyScreen = true;
}
void RivenGraphics::drawPLST(uint16 x) {
- Common::SeekableReadStream* plst = _vm->getRawData(ID_PLST, _vm->getCurCard());
+ Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, _vm->getCurCard());
uint16 index, id, left, top, right, bottom;
uint16 recordCount = plst->readUint16BE();
@@ -357,7 +405,7 @@ void RivenGraphics::updateScreen() {
}
void RivenGraphics::scheduleWaterEffect(uint16 sfxeID) {
- Common::SeekableReadStream *sfxeStream = _vm->getRawData(ID_SFXE, sfxeID);
+ Common::SeekableReadStream *sfxeStream = _vm->getResource(ID_SFXE, sfxeID);
if (sfxeStream->readUint16BE() != 'SL')
error ("Unknown sfxe tag");
@@ -491,115 +539,6 @@ void RivenGraphics::runScheduledTransition() {
_scheduledTransition = -1; // Clear scheduled transition
}
-void RivenGraphics::changeCursor(uint16 num) {
- // All of Riven's cursors are hardcoded. See riven_cursors.h for these definitions.
-
- switch (num) {
- case 1002:
- // Zip Mode
- CursorMan.replaceCursor(s_zipModeCursor, 16, 16, 8, 8, 0);
- CursorMan.replaceCursorPalette(s_zipModeCursorPalette, 1, ARRAYSIZE(s_zipModeCursorPalette) / 4);
- break;
- case 2003:
- // Hand Over Object
- CursorMan.replaceCursor(s_objectHandCursor, 16, 16, 8, 8, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 2004:
- // Grabbing/Using Object
- CursorMan.replaceCursor(s_grabbingHandCursor, 13, 13, 6, 6, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3000:
- // Standard Hand
- CursorMan.replaceCursor(s_standardHandCursor, 15, 16, 6, 0, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3001:
- // Pointing Left
- CursorMan.replaceCursor(s_pointingLeftCursor, 15, 13, 0, 3, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3002:
- // Pointing Right
- CursorMan.replaceCursor(s_pointingRightCursor, 15, 13, 14, 3, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3003:
- // Pointing Down (Palm Up)
- CursorMan.replaceCursor(s_pointingDownCursorPalmUp, 13, 16, 3, 15, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3004:
- // Pointing Up (Palm Up)
- CursorMan.replaceCursor(s_pointingUpCursorPalmUp, 13, 16, 3, 0, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3005:
- // Pointing Left (Curved)
- CursorMan.replaceCursor(s_pointingLeftCursorBent, 15, 13, 0, 5, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3006:
- // Pointing Right (Curved)
- CursorMan.replaceCursor(s_pointingRightCursorBent, 15, 13, 14, 5, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 3007:
- // Pointing Down (Palm Down)
- CursorMan.replaceCursor(s_pointingDownCursorPalmDown, 15, 16, 7, 15, 0);
- CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
- break;
- case 4001:
- // Red Marble
- CursorMan.replaceCursor(s_redMarbleCursor, 12, 12, 5, 5, 0);
- CursorMan.replaceCursorPalette(s_redMarbleCursorPalette, 1, ARRAYSIZE(s_redMarbleCursorPalette) / 4);
- break;
- case 4002:
- // Orange Marble
- CursorMan.replaceCursor(s_orangeMarbleCursor, 12, 12, 5, 5, 0);
- CursorMan.replaceCursorPalette(s_orangeMarbleCursorPalette, 1, ARRAYSIZE(s_orangeMarbleCursorPalette) / 4);
- break;
- case 4003:
- // Yellow Marble
- CursorMan.replaceCursor(s_yellowMarbleCursor, 12, 12, 5, 5, 0);
- CursorMan.replaceCursorPalette(s_yellowMarbleCursorPalette, 1, ARRAYSIZE(s_yellowMarbleCursorPalette) / 4);
- break;
- case 4004:
- // Green Marble
- CursorMan.replaceCursor(s_greenMarbleCursor, 12, 12, 5, 5, 0);
- CursorMan.replaceCursorPalette(s_greenMarbleCursorPalette, 1, ARRAYSIZE(s_greenMarbleCursorPalette) / 4);
- break;
- case 4005:
- // Blue Marble
- CursorMan.replaceCursor(s_blueMarbleCursor, 12, 12, 5, 5, 0);
- CursorMan.replaceCursorPalette(s_blueMarbleCursorPalette, 1, ARRAYSIZE(s_blueMarbleCursorPalette) / 4);
- break;
- case 4006:
- // Violet Marble
- CursorMan.replaceCursor(s_violetMarbleCursor, 12, 12, 5, 5, 0);
- CursorMan.replaceCursorPalette(s_violetMarbleCursorPalette, 1, ARRAYSIZE(s_violetMarbleCursorPalette) / 4);
- break;
- case 5000:
- // Pellet
- CursorMan.replaceCursor(s_pelletCursor, 8, 8, 4, 4, 0);
- CursorMan.replaceCursorPalette(s_pelletCursorPalette, 1, ARRAYSIZE(s_pelletCursorPalette) / 4);
- break;
- case 9000:
- // Hide Cursor
- CursorMan.showMouse(false);
- break;
- default:
- error("Cursor %d does not exist!", num);
- }
-
- if (num != 9000) // Show Cursor
- CursorMan.showMouse(true);
-
- // Should help in cases where we need to hide the cursor immediately.
- _vm->_system->updateScreen();
-}
-
void RivenGraphics::showInventory() {
// Don't redraw the inventory
if (_inventoryDrawn)
@@ -668,14 +607,13 @@ void RivenGraphics::clearInventoryArea() {
}
void RivenGraphics::drawInventoryImage(uint16 id, const Common::Rect *rect) {
- ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
- Graphics::Surface *surface = imageData->getSurface();
- delete imageData;
+ MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
+ mhkSurface->convertToTrueColor();
+ Graphics::Surface *surface = mhkSurface->getSurface();
_vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect->left, rect->top, surface->w, surface->h);
- surface->free();
- delete surface;
+ delete mhkSurface;
}
void RivenGraphics::drawRect(Common::Rect rect, bool active) {
@@ -692,100 +630,92 @@ void RivenGraphics::drawRect(Common::Rect rect, bool active) {
void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect) {
// Draw tBMP id from srcRect to dstRect
- ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getRawData(ID_TBMP, id));
- Graphics::Surface *surface = imageData->getSurface();
- delete imageData;
+ Graphics::Surface *surface = findImage(id)->getSurface();
assert(srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height());
for (uint16 i = 0; i < srcRect.height(); i++)
memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(srcRect.left, i + srcRect.top), srcRect.width() * surface->bytesPerPixel);
- surface->free();
- delete surface;
-
_dirtyScreen = true;
}
void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) {
- ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
- Graphics::Surface *surface = imageData->getSurface();
- delete imageData;
+ MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
+ mhkSurface->convertToTrueColor();
+ Graphics::Surface *surface = mhkSurface->getSurface();
assert(dstRect.width() == surface->w);
for (uint16 i = 0; i < surface->h; i++)
memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(0, i), surface->pitch);
- surface->free();
- delete surface;
-
+ delete mhkSurface;
_dirtyScreen = true;
}
-LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm) : _vm(vm) {
+LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm) : GraphicsManager(), _vm(vm) {
_bmpDecoder = (_vm->getGameType() == GType_LIVINGBOOKSV1) ? new OldMohawkBitmap() : new MohawkBitmap();
- _palette = new byte[256 * 4];
- memset(_palette, 0, 256 * 4);
}
LBGraphics::~LBGraphics() {
delete _bmpDecoder;
- delete[] _palette;
}
-void LBGraphics::copyImageToScreen(uint16 image, uint16 left, uint16 right) {
- if (_vm->getGameType() == GType_LIVINGBOOKSV1) {
- // Drawing images in the old format isn't supported (yet)
- ImageData *imageData = _bmpDecoder->decodeImage(_vm->wrapStreamEndian(ID_BMAP, image));
- delete imageData;
- } else {
- ImageData *imageData = _bmpDecoder->decodeImage(_vm->getRawData(ID_TBMP, image));
- imageData->_palette = _palette;
- Graphics::Surface *surface = imageData->getSurface();
- imageData->_palette = NULL; // Unset the palette so it doesn't get deleted
- delete imageData;
-
- uint16 width = MIN<int>(surface->w, 640);
- uint16 height = MIN<int>(surface->h, 480);
- _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, left, right, width, height);
- surface->free();
- delete surface;
-
- // FIXME: Remove this and update only at certain points
- _vm->_system->updateScreen();
- }
+MohawkSurface *LBGraphics::decodeImage(uint16 id) {
+ if (_vm->getGameType() == GType_LIVINGBOOKSV1)
+ return _bmpDecoder->decodeImage(_vm->wrapStreamEndian(ID_BMAP, id));
+
+ return _bmpDecoder->decodeImage(_vm->getResource(ID_TBMP, id));
+}
+
+void LBGraphics::copyImageToScreen(uint16 image, uint16 left, uint16 top) {
+ Graphics::Surface *surface = findImage(image)->getSurface();
+
+ uint16 width = MIN<int>(surface->w, _vm->_system->getWidth());
+ uint16 height = MIN<int>(surface->h, _vm->_system->getHeight());
+ _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, left, top, width, height);
+
+ // FIXME: Remove this and update only when necessary
+ _vm->_system->updateScreen();
}
void LBGraphics::setPalette(uint16 id) {
// Old Living Books games use the old CTBL-style palette format while newer
// games use the better tPAL format which can store partial palettes.
-
if (_vm->getGameType() == GType_LIVINGBOOKSV1) {
Common::SeekableSubReadStreamEndian *ctblStream = _vm->wrapStreamEndian(ID_CTBL, id);
uint16 colorCount = ctblStream->readUint16();
+ byte *palette = new byte[colorCount * 4];
for (uint16 i = 0; i < colorCount; i++) {
- _palette[i * 4] = ctblStream->readByte();
- _palette[i * 4 + 1] = ctblStream->readByte();
- _palette[i * 4 + 2] = ctblStream->readByte();
- _palette[i * 4 + 3] = ctblStream->readByte();
+ palette[i * 4] = ctblStream->readByte();
+ palette[i * 4 + 1] = ctblStream->readByte();
+ palette[i * 4 + 2] = ctblStream->readByte();
+ palette[i * 4 + 3] = ctblStream->readByte();
}
delete ctblStream;
+
+ _vm->_system->setPalette(palette, 0, colorCount);
+ delete[] palette;
} else {
- Common::SeekableReadStream *tpalStream = _vm->getRawData(ID_TPAL, id);
+ Common::SeekableReadStream *tpalStream = _vm->getResource(ID_TPAL, id);
uint16 colorStart = tpalStream->readUint16BE();
uint16 colorCount = tpalStream->readUint16BE();
+ byte *palette = new byte[colorCount * 4];
- for (uint16 i = colorStart; i < colorStart + colorCount; i++) {
- _palette[i * 4] = tpalStream->readByte();
- _palette[i * 4 + 1] = tpalStream->readByte();
- _palette[i * 4 + 2] = tpalStream->readByte();
- _palette[i * 4 + 3] = tpalStream->readByte();
+ for (uint16 i = 0; i < colorCount; i++) {
+ palette[i * 4] = tpalStream->readByte();
+ palette[i * 4 + 1] = tpalStream->readByte();
+ palette[i * 4 + 2] = tpalStream->readByte();
+ palette[i * 4 + 3] = tpalStream->readByte();
}
delete tpalStream;
+
+ _vm->_system->setPalette(palette, colorStart, colorCount);
+ delete[] palette;
}
}
diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h
index 9419aad277..38d174b481 100644
--- a/engines/mohawk/graphics.h
+++ b/engines/mohawk/graphics.h
@@ -30,6 +30,7 @@
#include "mohawk/livingbooks.h"
#include "common/file.h"
+#include "common/hashmap.h"
#include "graphics/pict.h"
#include "graphics/video/codecs/mjpeg.h"
@@ -40,57 +41,56 @@ class MohawkEngine_Riven;
class MohawkBitmap;
class MystBitmap;
-enum {
- kRivenOpenHandCursor = 2003,
- kRivenClosedHandCursor = 2004,
- kRivenMainCursor = 3000,
- kRivenPelletCursor = 5000,
- kRivenHideCursor = 9000
-};
+class MohawkSurface {
+public:
+ MohawkSurface();
+ MohawkSurface(Graphics::Surface *surface, byte *palette = NULL, int offsetX = 0, int offsetY = 0);
+ ~MohawkSurface();
+
+ // getSurface() returns the surface in the current format
+ // This will be the initial format unless convertToTrueColor() is called
+ Graphics::Surface *getSurface() const { return _surface; }
+ byte *getPalette() const { return _palette; }
+
+ // Convert the 8bpp image to the current screen format
+ // Does nothing if _surface is already >8bpp
+ void convertToTrueColor();
+
+ // Functions for OldMohawkBitmap offsets
+ // They both default to 0
+ int getOffsetX() const { return _offsetX; }
+ int getOffsetY() const { return _offsetY; }
+ void setOffsetX(int x) { _offsetX = x; }
+ void setOffsetY(int y) { _offsetY = y; }
-// 803-805 are animated, one large bmp which is in chunks
-// Other cursors (200, 300, 400, 500, 600, 700) are not the same in each stack
-enum {
- kDefaultMystCursor = 100, // The default hand
- kWhitePageCursor = 800, // Holding a white page
- kRedPageCursor = 801, // Holding a red page
- kBluePageCursor = 802, // Holding a blue page
- // kDroppingWhitePageAnimCursor = 803,
- // kDroppingRedPageAnimCursor = 804,
- // kDroppingBluePageAnimCursor = 805,
- kNewMatchCursor = 900, // Match that has not yet been lit
- kLitMatchCursor = 901, // Match that's burning
- kDeadMatchCursor = 902, // Match that's been extinguished
- kKeyCursor = 903, // Key in Lighthouse in Stoneship
- kRotateClockwiseCursor = 904, // Rotate gear clockwise (boiler on Myst)
- kRotateCounterClockwiseCursor = 905, // Rotate gear counter clockwise (boiler on Myst)
- kMystZipModeCursor = 999 // Zip Mode cursor
+private:
+ Graphics::Surface *_surface;
+ byte *_palette;
+ int _offsetX, _offsetY;
};
-// A simple struct to hold necessary image info
-class ImageData {
+class GraphicsManager {
public:
- ImageData() : _surface(0), _palette(0) {}
- ImageData(Graphics::Surface *surface, byte *palette = NULL) : _surface(surface), _palette(palette) {}
- ~ImageData() {
- if (_palette)
- free(_palette);
- if (_surface) {
- _surface->free();
- delete _surface;
- }
- }
-
- // getSurface() will convert to the current screen format, if it's not already
- // in that format. Makes it easy to support both 8bpp and 24bpp images.
- Graphics::Surface *getSurface();
-
- // These are still public in case the 8bpp surface needs to be accessed
- Graphics::Surface *_surface;
- byte *_palette;
+ GraphicsManager();
+ virtual ~GraphicsManager();
+
+ // Free all surfaces in the cache
+ void clearCache();
+
+protected:
+ // findImage will search the cache to find the image.
+ // If not found, it will call decodeImage to get a new one.
+ MohawkSurface *findImage(uint16 id);
+
+ // decodeImage will always return a new image.
+ virtual MohawkSurface *decodeImage(uint16 id) = 0;
+
+private:
+ // An image cache that stores images until clearCache() is called
+ Common::HashMap<uint16, MohawkSurface*> _cache;
};
-class MystGraphics {
+class MystGraphics : public GraphicsManager {
public:
MystGraphics(MohawkEngine_Myst*);
~MystGraphics();
@@ -98,17 +98,18 @@ public:
void loadExternalPictureFile(uint16 stack);
void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest);
void copyImageToScreen(uint16 image, Common::Rect dest);
- void showCursor();
- void hideCursor();
- void changeCursor(uint16);
+ void updateScreen();
void drawRect(Common::Rect rect, bool active);
+
+protected:
+ MohawkSurface *decodeImage(uint16 id);
+
private:
MohawkEngine_Myst *_vm;
MystBitmap *_bmpDecoder;
Graphics::PictDecoder *_pictDecoder;
Graphics::JPEGDecoder *_jpegDecoder;
- Graphics::PixelFormat _pixelFormat;
struct PictureFile {
uint32 pictureCount;
@@ -123,6 +124,10 @@ private:
Common::File picFile;
} _pictureFile;
+
+ Graphics::Surface *_mainScreen;
+ bool _dirtyScreen;
+ Graphics::PixelFormat _pixelFormat;
};
struct SFXERecord {
@@ -137,7 +142,7 @@ struct SFXERecord {
uint32 lastFrameTime;
};
-class RivenGraphics {
+class RivenGraphics : public GraphicsManager {
public:
RivenGraphics(MohawkEngine_Riven *vm);
~RivenGraphics();
@@ -145,7 +150,6 @@ public:
void copyImageToScreen(uint16, uint32, uint32, uint32, uint32);
void updateScreen();
bool _updatesEnabled;
- void changeCursor(uint16);
Common::Array<uint16> _activatedPLSTs;
void drawPLST(uint16 x);
void drawRect(Common::Rect rect, bool active);
@@ -165,6 +169,9 @@ public:
void showInventory();
void hideInventory();
+protected:
+ MohawkSurface *decodeImage(uint16 id);
+
private:
MohawkEngine_Riven *_vm;
MohawkBitmap *_bitmapDecoder;
@@ -187,7 +194,7 @@ private:
Graphics::PixelFormat _pixelFormat;
};
-class LBGraphics {
+class LBGraphics : public GraphicsManager {
public:
LBGraphics(MohawkEngine_LivingBooks *vm);
~LBGraphics();
@@ -195,10 +202,12 @@ public:
void copyImageToScreen(uint16 image, uint16 left = 0, uint16 top = 0);
void setPalette(uint16 id);
+protected:
+ MohawkSurface *decodeImage(uint16 id);
+
private:
MohawkBitmap *_bmpDecoder;
MohawkEngine_LivingBooks *_vm;
- byte *_palette;
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 515ee7c8ba..0ce8135508 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -63,7 +63,7 @@ Common::Error MohawkEngine_LivingBooks::run() {
debug("Setting screen size to %dx%d", _screenWidth, _screenHeight);
// TODO: Eventually move this to a LivingBooksGraphics class or similar
- initGraphics(_screenWidth, _screenHeight, true, NULL);
+ initGraphics(_screenWidth, _screenHeight, true);
loadIntro();
@@ -119,7 +119,7 @@ Common::Error MohawkEngine_LivingBooks::run() {
return Common::kNoError;
}
-void MohawkEngine_LivingBooks::loadBookInfo(Common::String filename) {
+void MohawkEngine_LivingBooks::loadBookInfo(const Common::String &filename) {
if (!_bookInfoFile.loadFromFile(filename))
error("Could not open %s as a config file", filename.c_str());
@@ -149,10 +149,12 @@ void MohawkEngine_LivingBooks::loadIntro() {
if (filename.empty())
filename = getFileNameFromConfig("Intro", "Page1.r");
- if (!filename.empty() && Common::File::exists(filename)) {
+ if (!filename.empty()) {
MohawkArchive *introArchive = createMohawkArchive();
- introArchive->open(filename);
- _mhk.push_back(introArchive);
+ if (introArchive->open(filename))
+ _mhk.push_back(introArchive);
+ else
+ delete introArchive;
}
filename = getFileNameFromConfig("Intro", "Page2");
@@ -160,16 +162,18 @@ void MohawkEngine_LivingBooks::loadIntro() {
if (filename.empty())
filename = getFileNameFromConfig("Intro", "Page2.r");
- if (!filename.empty() && Common::File::exists(filename)) {
+ if (!filename.empty()) {
MohawkArchive *coverArchive = createMohawkArchive();
- coverArchive->open(filename);
- _mhk.push_back(coverArchive);
+ if (coverArchive->open(filename))
+ _mhk.push_back(coverArchive);
+ else
+ delete coverArchive;
}
}
// Only 1 VSRN resource per stack, Id 1000
uint16 MohawkEngine_LivingBooks::getResourceVersion() {
- Common::SeekableReadStream *versionStream = getRawData(ID_VRSN, 1000);
+ Common::SeekableReadStream *versionStream = getResource(ID_VRSN, 1000);
if (versionStream->size() != 2)
warning("Version Record size mismatch");
@@ -264,42 +268,43 @@ void MohawkEngine_LivingBooks::loadANI(uint16 resourceId) {
}
Common::SeekableSubReadStreamEndian *MohawkEngine_LivingBooks::wrapStreamEndian(uint32 tag, uint16 id) {
- Common::SeekableReadStream *dataStream = getRawData(tag, id);
+ Common::SeekableReadStream *dataStream = getResource(tag, id);
return new Common::SeekableSubReadStreamEndian(dataStream, 0, dataStream->size(), isBigEndian(), DisposeAfterUse::YES);
}
-Common::String MohawkEngine_LivingBooks::getStringFromConfig(Common::String section, Common::String key) {
+Common::String MohawkEngine_LivingBooks::getStringFromConfig(const Common::String &section, const Common::String &key) {
Common::String x;
_bookInfoFile.getKey(key, section, x);
return removeQuotesFromString(x);
}
-int MohawkEngine_LivingBooks::getIntFromConfig(Common::String section, Common::String key) {
+int MohawkEngine_LivingBooks::getIntFromConfig(const Common::String &section, const Common::String &key) {
return atoi(getStringFromConfig(section, key).c_str());
}
-Common::String MohawkEngine_LivingBooks::getFileNameFromConfig(Common::String section, Common::String key) {
+Common::String MohawkEngine_LivingBooks::getFileNameFromConfig(const Common::String &section, const Common::String &key) {
Common::String x = getStringFromConfig(section, key);
return (getPlatform() == Common::kPlatformMacintosh) ? convertMacFileName(x) : convertWinFileName(x);
}
-Common::String MohawkEngine_LivingBooks::removeQuotesFromString(Common::String string) {
+Common::String MohawkEngine_LivingBooks::removeQuotesFromString(const Common::String &string) {
// The last char isn't necessarily a quote, the line could have "fade" in it,
// most likely representing to fade to that page. Hopefully it really is that
// obvious :P
// Some versions wrap in quotations, some don't...
- for (uint32 i = 0; i < string.size(); i++) {
- if (string[i] == '\"') {
- string.deleteChar(i);
+ Common::String tmp = string;
+ for (uint32 i = 0; i < tmp.size(); i++) {
+ if (tmp[i] == '\"') {
+ tmp.deleteChar(i);
i--;
}
}
- return string;
+ return tmp;
}
-Common::String MohawkEngine_LivingBooks::convertMacFileName(Common::String string) {
+Common::String MohawkEngine_LivingBooks::convertMacFileName(const Common::String &string) {
Common::String filename;
for (uint32 i = 1; i < string.size(); i++) { // First character should be ignored (another colon)
@@ -312,7 +317,7 @@ Common::String MohawkEngine_LivingBooks::convertMacFileName(Common::String strin
return filename;
}
-Common::String MohawkEngine_LivingBooks::convertWinFileName(Common::String string) {
+Common::String MohawkEngine_LivingBooks::convertWinFileName(const Common::String &string) {
Common::String filename;
for (uint32 i = 0; i < string.size(); i++) {
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index 6bd0729bbf..0305c92c6a 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -31,6 +31,7 @@
#include "mohawk/graphics.h"
#include "common/config-file.h"
+#include "common/substream.h"
namespace Mohawk {
@@ -60,7 +61,7 @@ private:
uint16 _curPage;
Common::String getBookInfoFileName() const;
- void loadBookInfo(Common::String filename);
+ void loadBookInfo(const Common::String &filename);
void loadIntro();
uint16 getResourceVersion();
@@ -75,14 +76,14 @@ private:
Common::String _copyright;
// String Manipulation Functions
- Common::String removeQuotesFromString(Common::String string);
- Common::String convertMacFileName(Common::String string);
- Common::String convertWinFileName(Common::String string);
+ Common::String removeQuotesFromString(const Common::String &string);
+ Common::String convertMacFileName(const Common::String &string);
+ Common::String convertWinFileName(const Common::String &string);
// Configuration File Functions
- Common::String getStringFromConfig(Common::String section, Common::String key);
- int getIntFromConfig(Common::String section, Common::String key);
- Common::String getFileNameFromConfig(Common::String section, Common::String key);
+ Common::String getStringFromConfig(const Common::String &section, const Common::String &key);
+ int getIntFromConfig(const Common::String &section, const Common::String &key);
+ Common::String getFileNameFromConfig(const Common::String &section, const Common::String &key);
// Platform/Version functions
bool isBigEndian() const { return getGameType() == GType_LIVINGBOOKSV3 || getPlatform() == Common::kPlatformMacintosh; }
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index bb79d4abac..2304121c87 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/mohawk
MODULE_OBJS = \
bitmap.o \
console.o \
+ cursors.o \
detection.o \
dialogs.o \
graphics.o \
diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp
index 6b4d8bb2d2..623f251e1a 100644
--- a/engines/mohawk/mohawk.cpp
+++ b/engines/mohawk/mohawk.cpp
@@ -33,6 +33,7 @@
#include "base/version.h"
#include "mohawk/mohawk.h"
+#include "mohawk/cursors.h"
#include "mohawk/dialogs.h"
#include "mohawk/sound.h"
#include "mohawk/video.h"
@@ -47,12 +48,18 @@ MohawkEngine::MohawkEngine(OSystem *syst, const MohawkGameDescription *gamedesc)
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+
+ _sound = 0;
+ _video = 0;
+ _pauseDialog = 0;
+ _cursor = 0;
}
MohawkEngine::~MohawkEngine() {
delete _sound;
delete _video;
delete _pauseDialog;
+ delete _cursor;
for (uint32 i = 0; i < _mhk.size(); i++)
delete _mhk[i];
@@ -84,12 +91,12 @@ void MohawkEngine::pauseGame() {
runDialog(*_pauseDialog);
}
-Common::SeekableReadStream *MohawkEngine::getRawData(uint32 tag, uint16 id) {
+Common::SeekableReadStream *MohawkEngine::getResource(uint32 tag, uint16 id) {
for (uint32 i = 0; i < _mhk.size(); i++)
if (_mhk[i]->hasResource(tag, id))
- return _mhk[i]->getRawData(tag, id);
+ return _mhk[i]->getResource(tag, id);
- error ("Could not find a \'%s\' resource with ID %04x", tag2str(tag), id);
+ error("Could not find a '%s' resource with ID %04x", tag2str(tag), id);
return NULL;
}
@@ -101,13 +108,30 @@ bool MohawkEngine::hasResource(uint32 tag, uint16 id) {
return false;
}
+bool MohawkEngine::hasResource(uint32 tag, const Common::String &resName) {
+ for (uint32 i = 0; i < _mhk.size(); i++)
+ if (_mhk[i]->hasResource(tag, resName))
+ return true;
+
+ return false;
+}
+
uint32 MohawkEngine::getResourceOffset(uint32 tag, uint16 id) {
for (uint32 i = 0; i < _mhk.size(); i++)
if (_mhk[i]->hasResource(tag, id))
return _mhk[i]->getOffset(tag, id);
- error ("Could not find a \'%s\' resource with ID %04x", tag2str(tag), id);
+ error("Could not find a '%s' resource with ID %04x", tag2str(tag), id);
return 0;
}
+uint16 MohawkEngine::findResourceID(uint32 tag, const Common::String &resName) {
+ for (uint32 i = 0; i < _mhk.size(); i++)
+ if (_mhk[i]->hasResource(tag, resName))
+ return _mhk[i]->findResourceID(tag, resName);
+
+ error("Could not find a '%s' resource matching name '%s'", tag2str(tag), resName.c_str());
+ return 0xFFFF;
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h
index e4d26d16f7..ed59f727f3 100644
--- a/engines/mohawk/mohawk.h
+++ b/engines/mohawk/mohawk.h
@@ -37,6 +37,15 @@ namespace Common {
class SeekableReadStream;
}
+/**
+ * This is the namespace of the Mohawk engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Myst
+ * - Riven: The Sequel to Myst
+ */
namespace Mohawk {
enum MohawkGameType {
@@ -67,6 +76,7 @@ class Sound;
class PauseDialog;
class MohawkArchive;
class VideoManager;
+class CursorManager;
class MohawkEngine : public ::Engine {
protected:
@@ -78,8 +88,9 @@ public:
// Detection related functions
const MohawkGameDescription *_gameDescription;
- const char* getGameId() const;
+ const char *getGameId() const;
uint32 getFeatures() const;
+ const char *getAppName() const;
uint16 getVersion() const;
Common::Platform getPlatform() const;
uint8 getGameType() const;
@@ -89,10 +100,13 @@ public:
Sound *_sound;
VideoManager *_video;
+ CursorManager *_cursor;
- virtual Common::SeekableReadStream *getRawData(uint32 tag, uint16 id);
+ virtual Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
bool hasResource(uint32 tag, uint16 id);
+ bool hasResource(uint32 tag, const Common::String &resName);
uint32 getResourceOffset(uint32 tag, uint16 id);
+ uint16 findResourceID(uint32 type, const Common::String &resName);
void pauseGame();
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index 6ed7a313a0..b4ec087b61 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -27,9 +27,9 @@
#include "common/debug-channels.h"
#include "common/translation.h"
+#include "mohawk/cursors.h"
#include "mohawk/graphics.h"
#include "mohawk/myst.h"
-#include "mohawk/myst_scripts.h"
#include "mohawk/myst_saveload.h"
#include "mohawk/dialogs.h"
#include "mohawk/resource.h"
@@ -64,6 +64,14 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription
_needsUpdate = false;
_curResource = -1;
+ _gfx = NULL;
+ _console = NULL;
+ _scriptParser = NULL;
+ _varStore = NULL;
+ _saveLoad = NULL;
+ _loadDialog = NULL;
+ _optionsDialog = NULL;
+
_cursorHintCount = 0;
_cursorHints = NULL;
@@ -83,6 +91,8 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription
}
MohawkEngine_Myst::~MohawkEngine_Myst() {
+ DebugMan.clearAllDebugChannels();
+
delete _gfx;
delete _console;
delete _scriptParser;
@@ -90,10 +100,17 @@ MohawkEngine_Myst::~MohawkEngine_Myst() {
delete _saveLoad;
delete _loadDialog;
delete _optionsDialog;
+
+ delete[] _cursorHints;
+
delete[] _view.conditionalImages;
delete[] _view.scriptResources;
- delete[] _cursorHints;
- _resources.clear();
+
+ while(!_resources.empty()) {
+ MystResource *temp = _resources.back();
+ _resources.pop_back();
+ delete temp;
+ }
}
// Uses cached data objects in preference to disk access
@@ -105,7 +122,7 @@ Common::SeekableReadStream *MohawkEngine_Myst::getRawData(uint32 tag, uint16 id)
for (uint32 i = 0; i < _mhk.size(); i++)
if (_mhk[i]->hasResource(tag, id)) {
- ret = _mhk[i]->getRawData(tag, id);
+ ret = _mhk[i]->getResource(tag, id);
_cache.add(tag, id, ret);
return ret;
}
@@ -121,19 +138,19 @@ void MohawkEngine_Myst::cachePreload(uint32 tag, uint16 id) {
for (uint32 i = 0; i < _mhk.size(); i++) {
// Check for MJMP in Myst ME
if ((getFeatures() & GF_ME) && tag == ID_MSND && _mhk[i]->hasResource(ID_MJMP, id)) {
- Common::SeekableReadStream *tempData = _mhk[i]->getRawData(ID_MJMP, id);
+ Common::SeekableReadStream *tempData = _mhk[i]->getResource(ID_MJMP, id);
uint16 msndId = tempData->readUint16LE();
delete tempData;
// We've found where the real MSND data is, so go get that
- tempData = _mhk[i]->getRawData(tag, msndId);
+ tempData = _mhk[i]->getResource(tag, msndId);
_cache.add(tag, id, tempData);
delete tempData;
return;
}
if (_mhk[i]->hasResource(tag, id)) {
- Common::SeekableReadStream *tempData = _mhk[i]->getRawData(tag, id);
+ Common::SeekableReadStream *tempData = _mhk[i]->getResource(tag, id);
_cache.add(tag, id, tempData);
delete tempData;
return;
@@ -170,7 +187,7 @@ static const char *mystFiles[] = {
// qtw/myst/gar4wbf1.mov: gar4wbf2.mov has two butterflies instead of one
// qtw/myst/libelev.mov: libup.mov is basically the same with sound
-Common::String MohawkEngine_Myst::wrapMovieFilename(Common::String movieName, uint16 stack) {
+Common::String MohawkEngine_Myst::wrapMovieFilename(const Common::String &movieName, uint16 stack) {
// The Macintosh release of Myst ME stores its videos in a different folder
if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh)
return Common::String("CD Data/m/") + movieName + ".mov";
@@ -222,6 +239,7 @@ Common::Error MohawkEngine_Myst::run() {
_loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"));
_loadDialog->setSaveMode(false);
_optionsDialog = new MystOptionsDialog(this);
+ _cursor = new MystCursorManager(this);
// Start us on the first stack.
if (getGameType() == GType_MAKINGOF)
@@ -248,7 +266,8 @@ Common::Error MohawkEngine_Myst::run() {
// Load Help System (Masterpiece Edition Only)
if (getFeatures() & GF_ME) {
MohawkArchive *mhk = new MohawkArchive();
- mhk->open("help.dat");
+ if (!mhk->open("help.dat"))
+ error("Could not load help.dat");
_mhk.push_back(mhk);
}
@@ -256,8 +275,8 @@ Common::Error MohawkEngine_Myst::run() {
loadHelp(10000);
// Set the cursor
- _gfx->changeCursor(_currentCursor);
- _gfx->showCursor();
+ _cursor->setCursor(_currentCursor);
+ _cursor->showCursor();
Common::Event event;
while (!shouldQuit()) {
@@ -282,9 +301,7 @@ Common::Error MohawkEngine_Myst::run() {
_resources[_curResource]->handleMouseUp();
}
- for (uint16 i = 0; i < _resources.size(); i++)
- if (_resources[i]->isEnabled())
- _resources[i]->drawDataToScreen();
+ drawResourceImages();
break;
case Common::EVENT_LBUTTONDOWN:
if (_curResource >= 0) {
@@ -346,14 +363,17 @@ void MohawkEngine_Myst::changeToStack(uint16 stack) {
_mhk[0] = new MohawkArchive();
}
- _mhk[0]->open(mystFiles[_curStack]);
+ if (!_mhk[0]->open(mystFiles[_curStack]))
+ error("Could not open %s", mystFiles[_curStack]);
if (getPlatform() == Common::kPlatformMacintosh)
_gfx->loadExternalPictureFile(_curStack);
_runExitScript = false;
+ // Clear the resource cache and the image cache
_cache.clear();
+ _gfx->clearCache();
}
void MohawkEngine_Myst::changeToCard(uint16 card) {
@@ -371,7 +391,9 @@ void MohawkEngine_Myst::changeToCard(uint16 card) {
unloadCard();
+ // Clear the resource cache and image cache
_cache.clear();
+ _gfx->clearCache();
_curCard = card;
@@ -433,6 +455,9 @@ void MohawkEngine_Myst::changeToCard(uint16 card) {
error("Unknown sound action %d", soundAction);
}
+ // Update the images of each area too
+ drawResourceImages();
+
// TODO: Handle Script Resources
// Run the entrance script (if present)
@@ -685,43 +710,11 @@ void MohawkEngine_Myst::runInitScript() {
debugC(kDebugINIT, "Running INIT script");
Common::SeekableReadStream *initStream = getRawData(ID_INIT, _view.init);
-
- uint16 scriptCount = initStream->readUint16LE();
-
- debugC(kDebugINIT, "\tOpcode Count: %d", scriptCount);
-
- MystScriptEntry *scripts = new MystScriptEntry[scriptCount];
-
- for (uint16 i = 0; i < scriptCount; i++) {
- // TODO: u0 is likely variable reference for boolean to
- // determine whether or not to execute opcode
- uint16 u0 = initStream->readUint16LE();
- scripts[i].opcode = initStream->readUint16LE();
- // If variable indicates not to execute opcode, rewrite to NOP
- //if (!_varStore->getVar(u0))
- // scripts[i].opcode = 0xFFFF;
- scripts[i].var = initStream->readUint16LE();
- scripts[i].numValues = initStream->readUint16LE();
- scripts[i].values = new uint16[scripts[i].numValues];
-
- debugC(kDebugINIT, "\tu0: %d", u0);
- debugC(kDebugINIT, "\tOpcode %d: %s", i, _scriptParser->getOpcodeDesc(scripts[i].opcode));
- debugC(kDebugINIT, "\t\tUses Variable %d", scripts[i].var);
- debugC(kDebugINIT, "\t\tHas %d Arguments:", scripts[i].numValues);
-
- for (uint16 j = 0; j < scripts[i].numValues; j++) {
- scripts[i].values[j] = initStream->readUint16LE();
- debugC(kDebugINIT, "\t\tArgument %d: %d", j, scripts[i].values[j]);
- }
- }
-
+ MystScript script = _scriptParser->readScript(initStream, kMystScriptInit);
delete initStream;
- _scriptParser->runScript(scriptCount, scripts);
-
- for (uint16 i = 0; i < scriptCount; i++)
- delete[] scripts[i].values;
- delete[] scripts;
+ _scriptParser->runScript(script);
+ _gfx->updateScreen();
}
void MohawkEngine_Myst::runExitScript() {
@@ -733,48 +726,11 @@ void MohawkEngine_Myst::runExitScript() {
debugC(kDebugEXIT, "Running EXIT script");
Common::SeekableReadStream *exitStream = getRawData(ID_EXIT, _view.exit);
-
- uint16 scriptCount = exitStream->readUint16LE();
-
- debugC(kDebugEXIT, "\tOpcode Count: %d", scriptCount);
-
- MystScriptEntry *scripts = new MystScriptEntry[scriptCount];
-
- for (uint16 i = 0; i < scriptCount; i++) {
- // TODO: u0 is likely variable reference for boolean to
- // to determine whether or not to execute opcode (i.e. door
- // close noises only when door is open).
- uint16 u0 = exitStream->readUint16LE();
- scripts[i].opcode = exitStream->readUint16LE();
- // If variable indicates not to execute opcode, rewrite to NOP
- //if (!_varStore->getVar(u0))
- // scripts[i].opcode = 0xFFFF;
- scripts[i].var = exitStream->readUint16LE();
- scripts[i].numValues = exitStream->readUint16LE();
- scripts[i].values = new uint16[scripts[i].numValues];
-
- debugC(kDebugEXIT, "\tu0: %d", u0);
- debugC(kDebugEXIT, "\tOpcode %d: %s", i, _scriptParser->getOpcodeDesc(scripts[i].opcode));
- debugC(kDebugEXIT, "\t\tUses Variable %d", scripts[i].var);
- debugC(kDebugEXIT, "\t\tHas %d Arguments:", scripts[i].numValues);
-
- for (uint16 j = 0; j < scripts[i].numValues; j++) {
- scripts[i].values[j] = exitStream->readUint16LE();
- debugC(kDebugEXIT, "\t\tArgument %d: %d", j, scripts[i].values[j]);
- }
-
- uint16 u1 = exitStream->readUint16LE();
- if (u1 != 1)
- warning("Myst EXIT u1 not 1");
- }
-
+ MystScript script = _scriptParser->readScript(exitStream, kMystScriptExit);
delete exitStream;
- _scriptParser->runScript(scriptCount, scripts);
-
- for (uint16 i = 0; i < scriptCount; i++)
- delete[] scripts[i].values;
- delete[] scripts;
+ _scriptParser->runScript(script);
+ _gfx->updateScreen();
}
void MohawkEngine_Myst::loadHelp(uint16 id) {
@@ -868,7 +824,7 @@ void MohawkEngine_Myst::loadCursorHints() {
void MohawkEngine_Myst::setMainCursor(uint16 cursor) {
_currentCursor = _mainCursor = cursor;
- _gfx->changeCursor(_currentCursor);
+ _cursor->setCursor(_currentCursor);
}
void MohawkEngine_Myst::checkCursorHints() {
@@ -887,7 +843,7 @@ void MohawkEngine_Myst::checkCursorHints() {
_currentCursor = _cursorHints[i].variableHint.values[var_value];
if (_currentCursor == 0)
_currentCursor = _mainCursor;
- _gfx->changeCursor(_currentCursor);
+ _cursor->setCursor(_currentCursor);
}
} else if (_currentCursor != _cursorHints[i].cursor) {
if (_cursorHints[i].cursor == 0)
@@ -895,14 +851,14 @@ void MohawkEngine_Myst::checkCursorHints() {
else
_currentCursor = _cursorHints[i].cursor;
- _gfx->changeCursor(_currentCursor);
+ _cursor->setCursor(_currentCursor);
}
return;
}
if (_currentCursor != _mainCursor) {
_currentCursor = _mainCursor;
- _gfx->changeCursor(_currentCursor);
+ _cursor->setCursor(_currentCursor);
}
}
@@ -913,6 +869,15 @@ void MohawkEngine_Myst::setResourceEnabled(uint16 resourceId, bool enable) {
warning("Attempt to change unknown resource enable state");
}
+void MohawkEngine_Myst::drawResourceImages() {
+ for (uint16 i = 0; i < _resources.size(); i++)
+ if (_resources[i]->isEnabled())
+ _resources[i]->drawDataToScreen();
+
+ // Make sure the screen is updated
+ _gfx->updateScreen();
+}
+
static MystResource *loadResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) {
uint16 type = rlstStream->readUint16LE();
@@ -949,7 +914,11 @@ static MystResource *loadResource(MohawkEngine_Myst *vm, Common::SeekableReadStr
}
void MohawkEngine_Myst::loadResources() {
- _resources.clear();
+ while(!_resources.empty()) {
+ MystResource *temp = _resources.back();
+ _resources.pop_back();
+ delete temp;
+ }
if (!_view.rlst) {
debugC(kDebugResource, "No RLST present");
@@ -1011,6 +980,9 @@ MystResource::MystResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rl
(_flags & kMystSubimageEnableFlag) != 0);
}
+MystResource::~MystResource() {
+}
+
void MystResource::handleMouseUp() {
if (_dest != 0)
_vm->changeToCard(_dest);
@@ -1021,37 +993,15 @@ void MystResource::handleMouseUp() {
MystResourceType5::MystResourceType5(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) {
debugC(kDebugResource, "\tResource Type 5 Script:");
- _scriptCount = rlstStream->readUint16LE();
-
- debugC(kDebugResource, "\tOpcode Count: %d", _scriptCount);
-
- if (_scriptCount == 0)
- return;
-
- _scripts = new MystScriptEntry[_scriptCount];
- for (uint16 i = 0; i < _scriptCount; i++) {
- _scripts[i].opcode = rlstStream->readUint16LE();
- _scripts[i].var = rlstStream->readUint16LE();
- _scripts[i].numValues = rlstStream->readUint16LE();
- _scripts[i].values = new uint16[_scripts[i].numValues];
-
- debugC(kDebugResource, "\tOpcode %d: %s", i, _vm->_scriptParser->getOpcodeDesc(_scripts[i].opcode));
- debugC(kDebugResource, "\t\tUses Variable %d", _scripts[i].var);
- debugC(kDebugResource, "\t\tHas %d Arguments:", _scripts[i].numValues);
-
- for (uint16 j = 0; j < _scripts[i].numValues; j++) {
- _scripts[i].values[j] = rlstStream->readUint16LE();
- debugC(kDebugResource, "\t\tArgument %d: %d", j, _scripts[i].values[j]);
- }
- }
+ _script = vm->_scriptParser->readScript(rlstStream, kMystScriptNormal);
}
void MystResourceType5::handleMouseUp() {
- _vm->_scriptParser->runScript(_scriptCount, _scripts, this);
+ _vm->_scriptParser->runScript(_script, this);
}
// 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 MystResourceType6::convertMystVideoName(const Common::String &name) {
Common::String temp;
for (uint32 i = 1; i < name.size(); i++) {
@@ -1133,6 +1083,14 @@ MystResourceType7::MystResourceType7(MohawkEngine_Myst *vm, Common::SeekableRead
_subResources.push_back(loadResource(vm, rlstStream, this));
}
+MystResourceType7::~MystResourceType7() {
+ while(!_subResources.empty()) {
+ MystResource *temp = _subResources.back();
+ _subResources.pop_back();
+ delete temp;
+ }
+}
+
// TODO: All these functions to switch subresource are very similar.
// Find way to share code (function pointer pass?)
void MystResourceType7::drawDataToScreen() {
@@ -1273,9 +1231,12 @@ MystResourceType8::MystResourceType8(MohawkEngine_Myst *vm, Common::SeekableRead
_subImages[i].rect.right = rlstStream->readSint16LE();
_subImages[i].rect.bottom = rlstStream->readSint16LE();
} else {
- _subImages[i].rect.top = 0;
- _subImages[i].rect.right = 0;
- _subImages[i].rect.bottom = 0;
+ // 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;
}
debugC(kDebugResource, "\twdib: %d", _subImages[i].wdib);
@@ -1286,6 +1247,10 @@ MystResourceType8::MystResourceType8(MohawkEngine_Myst *vm, Common::SeekableRead
}
}
+MystResourceType8::~MystResourceType8() {
+ delete[] _subImages;
+}
+
void MystResourceType8::drawDataToScreen() {
// Need to call overidden Type 7 function to ensure
// switch section is processed correctly.
@@ -1336,24 +1301,7 @@ void MystResourceType8::drawDataToScreen() {
} else
imageToDraw = _subImages[subImageId].wdib;
- if (_subImages[subImageId].rect.left == -1)
- _vm->_gfx->copyImageSectionToScreen(imageToDraw, _rect, _rect);
- //vm->_gfx->copyImageToScreen(imageToDraw, Common::Rect(0, 0, 544, 333));
- // TODO: Think this is the case when the image is full screen.. need to modify graphics to add functions for returning size of image.
- // This is not right either...
- //else if (_rect.width() != _subImages[draw_subimage_id].rect.width() || _rect.height() != _subImages[draw_subimage_id].rect.height())
- // HACK: Hardcode cases of this until general rule can be ascertained
- // These cases seem to have the source rect in the wdib with an vertical i.e. top+X, bottom+X where X is a constant, but could
- // be negative, translations, when in fact both the source and dest should be equal...
- //else if ((vm->getCurStack() == kSeleniticStack && vm->getCurCard() == 1155 && tmp == 1) || // X=
- // (vm->getCurStack() == kSeleniticStack && vm->getCurCard() == 1225 && tmp == 1) || // X=
- // (vm->getCurStack() == kMystStack && vm->getCurCard() == 4247 && tmp == 0) || // X=
- // (vm->getCurStack() == kChannelwoodStack && vm->getCurCard() == 3161 && tmp == 0)) // X=
- // vm->_gfx->copyImageSectionToScreen(imageToDraw, _rect, _rect);
- // // TODO: Small vertical movement remains on change. Suspect off by one error from these to real
- // // solution.
- else
- _vm->_gfx->copyImageSectionToScreen(imageToDraw, _subImages[subImageId].rect, _rect);
+ _vm->_gfx->copyImageSectionToScreen(imageToDraw, _subImages[subImageId].rect, _rect);
}
}
@@ -1396,9 +1344,9 @@ MystResourceType10::MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableRe
// TODO: Not sure about order of Mouse Down, Mouse Drag and Mouse Up
// Or whether this is slightly different...
- printf("Type 10 _mouseDownOpcode: %d\n", _mouseDownOpcode);
- printf("Type 10 _mouseDragOpcode: %d\n", _mouseDragOpcode);
- printf("Type 10 _mouseUpOpcode: %d\n", _mouseUpOpcode);
+ debugCN(kDebugResource, "Type 10 _mouseDownOpcode: %d\n", _mouseDownOpcode);
+ debugCN(kDebugResource, "Type 10 _mouseDragOpcode: %d\n", _mouseDragOpcode);
+ debugCN(kDebugResource, "Type 10 _mouseUpOpcode: %d\n", _mouseUpOpcode);
for (byte i = 0; i < 4; i++) {
debugC(kDebugResource, "\tList %d:", i);
@@ -1416,6 +1364,11 @@ MystResourceType10::MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableRe
warning("TODO: Card contains Type 10 Resource - Function not yet implemented");
}
+MystResourceType10::~MystResourceType10() {
+ for (byte i = 0; i < 4; i++)
+ delete[] _lists[i].list;
+}
+
void MystResourceType10::handleMouseUp() {
// TODO
}
@@ -1452,9 +1405,9 @@ MystResourceType11::MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableRe
// TODO: Not sure about order of Mouse Down, Mouse Drag and Mouse Up
// Or whether this is slightly different...
- printf("Type 11 _mouseDownOpcode: %d\n", _mouseDownOpcode);
- printf("Type 11 _mouseDragOpcode: %d\n", _mouseDragOpcode);
- printf("Type 11 _mouseUpOpcode: %d\n", _mouseUpOpcode);
+ debugCN(kDebugResource, "Type 11 _mouseDownOpcode: %d\n", _mouseDownOpcode);
+ debugCN(kDebugResource, "Type 11 _mouseDragOpcode: %d\n", _mouseDragOpcode);
+ debugCN(kDebugResource, "Type 11 _mouseUpOpcode: %d\n", _mouseUpOpcode);
for (byte i = 0; i < 3; i++) {
debugC(kDebugResource, "\tList %d:", i);
@@ -1472,6 +1425,11 @@ MystResourceType11::MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableRe
warning("TODO: Card contains Type 11 Resource - Function not yet implemented");
}
+MystResourceType11::~MystResourceType11() {
+ for (byte i = 0; i < 3; i++)
+ delete[] _lists[i].list;
+}
+
void MystResourceType11::handleMouseUp() {
// TODO
@@ -1511,14 +1469,14 @@ MystResourceType12::MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableRe
// TODO: Think that u0 and u1 are animation frames to be
// drawn for var == 0 and var == 1
- printf("Type 12 _state0Frame: %d\n", _state0Frame);
- printf("Type 12 _state1Frame: %d\n", _state1Frame);
+ debugCN(kDebugResource, "Type 12 _state0Frame: %d\n", _state0Frame);
+ debugCN(kDebugResource, "Type 12 _state1Frame: %d\n", _state1Frame);
// TODO: Not sure about order of Mouse Down, Mouse Drag and Mouse Up
// Or whether this is slightly different...
- printf("Type 12 _mouseDownOpcode: %d\n", _mouseDownOpcode);
- printf("Type 12 _mouseDragOpcode: %d\n", _mouseDragOpcode);
- printf("Type 12 _mouseUpOpcode: %d\n", _mouseUpOpcode);
+ debugCN(kDebugResource, "Type 12 _mouseDownOpcode: %d\n", _mouseDownOpcode);
+ debugCN(kDebugResource, "Type 12 _mouseDragOpcode: %d\n", _mouseDragOpcode);
+ debugCN(kDebugResource, "Type 12 _mouseUpOpcode: %d\n", _mouseUpOpcode);
for (byte i = 0; i < 3; i++) {
debugC(kDebugResource, "\tList %d:", i);
@@ -1557,10 +1515,16 @@ MystResourceType12::MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableRe
_doAnimation = false;
}
+MystResourceType12::~MystResourceType12() {
+ for (byte i = 0; i < 3; i++)
+ delete[] _lists[i].list;
+}
+
void MystResourceType12::handleAnimation() {
// TODO: Probably not final version. Variable/Type 11 Controlled?
if (_doAnimation) {
_vm->_gfx->copyImageToScreen(_currentFrame++, _frameRect);
+ _vm->_gfx->updateScreen();
if ((_currentFrame - _firstFrame) >= _numFrames)
_doAnimation = false;
}
diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h
index 7486e559bb..3126fdd4fc 100644
--- a/engines/mohawk/myst.h
+++ b/engines/mohawk/myst.h
@@ -30,6 +30,7 @@
#include "mohawk/mohawk.h"
#include "mohawk/resource_cache.h"
#include "mohawk/myst_vars.h"
+#include "mohawk/myst_scripts.h"
#include "gui/saveload.h"
@@ -147,17 +148,10 @@ struct MystView {
uint16 exit;
};
-struct MystScriptEntry {
- uint16 opcode;
- uint16 var;
- uint16 numValues;
- uint16 *values;
-};
-
class MystResource {
public:
MystResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResource() {}
+ virtual ~MystResource();
MystResource *_parent;
@@ -191,8 +185,7 @@ public:
void handleMouseUp();
protected:
- uint16 _scriptCount;
- MystScriptEntry *_scripts;
+ MystScript _script;
};
class MystResourceType6 : public MystResourceType5 {
@@ -201,7 +194,7 @@ public:
void handleAnimation();
protected:
- static Common::String convertMystVideoName(Common::String name);
+ static Common::String convertMystVideoName(const Common::String &name);
Common::String _videoFile;
uint16 _left;
uint16 _top;
@@ -218,7 +211,7 @@ private:
struct MystResourceType7 : public MystResource {
public:
MystResourceType7(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResourceType7() {}
+ virtual ~MystResourceType7();
virtual void drawDataToScreen();
virtual void handleAnimation();
@@ -237,6 +230,7 @@ protected:
class MystResourceType8 : public MystResourceType7 {
public:
MystResourceType8(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
+ virtual ~MystResourceType8();
void drawDataToScreen();
uint16 getType8Var();
@@ -254,6 +248,7 @@ protected:
class MystResourceType10 : public MystResourceType8 {
public:
MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
+ virtual ~MystResourceType10();
void handleMouseUp();
protected:
@@ -273,6 +268,7 @@ protected:
class MystResourceType11 : public MystResourceType8 {
public:
MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
+ virtual ~MystResourceType11();
void handleMouseUp();
protected:
@@ -292,6 +288,7 @@ protected:
class MystResourceType12 : public MystResourceType8 {
public:
MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
+ virtual ~MystResourceType12();
void handleAnimation();
void handleMouseUp();
@@ -346,7 +343,7 @@ public:
Common::SeekableReadStream *getRawData(uint32 tag, uint16 id);
- Common::String wrapMovieFilename(Common::String movieName, uint16 stack);
+ Common::String wrapMovieFilename(const Common::String &movieName, uint16 stack);
void reloadSaveList();
void runLoadDialog();
@@ -408,6 +405,7 @@ private:
void drawResourceRects();
void checkCurrentResource();
int16 _curResource;
+ void drawResourceImages();
uint16 _cursorHintCount;
MystCursorHint *_cursorHints;
diff --git a/engines/mohawk/myst_saveload.cpp b/engines/mohawk/myst_saveload.cpp
index b3ce5adb3a..50a2c9b023 100644
--- a/engines/mohawk/myst_saveload.cpp
+++ b/engines/mohawk/myst_saveload.cpp
@@ -43,12 +43,12 @@ Common::StringArray MystSaveLoad::generateSaveGameList() {
return _saveFileMan->listSavefiles("*.mys");
}
-bool MystSaveLoad::loadGame(Common::String filename) {
+bool MystSaveLoad::loadGame(const Common::String &filename) {
if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo
return false;
- Common::InSaveFile *loadFile;
- if (!(loadFile = _saveFileMan->openForLoading(filename.c_str())))
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
+ if (!loadFile)
return false;
debugC(kDebugSaveLoad, "Loading game from \'%s\'", filename.c_str());
@@ -324,13 +324,14 @@ bool MystSaveLoad::loadGame(Common::String filename) {
return true;
}
-bool MystSaveLoad::saveGame(Common::String filename) {
+bool MystSaveLoad::saveGame(const Common::String &fname) {
+ Common::String filename(fname);
// Make sure we have the right extension
if (!filename.hasSuffix(".mys") && !filename.hasSuffix(".MYS"))
filename += ".mys";
- Common::OutSaveFile *saveFile;
- if (!(saveFile = _saveFileMan->openForSaving(filename.c_str())))
+ Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
+ if (!saveFile)
return false;
debugC(kDebugSaveLoad, "Saving game to \'%s\'", filename.c_str());
@@ -431,7 +432,7 @@ bool MystSaveLoad::saveGame(Common::String filename) {
return true;
}
-void MystSaveLoad::deleteSave(Common::String saveName) {
+void MystSaveLoad::deleteSave(const Common::String &saveName) {
debugC(kDebugSaveLoad, "Deleting save file \'%s\'", saveName.c_str());
_saveFileMan->removeSavefile(saveName.c_str());
}
diff --git a/engines/mohawk/myst_saveload.h b/engines/mohawk/myst_saveload.h
index cbb9557db5..33dd9a7e07 100644
--- a/engines/mohawk/myst_saveload.h
+++ b/engines/mohawk/myst_saveload.h
@@ -195,9 +195,9 @@ public:
~MystSaveLoad();
Common::StringArray generateSaveGameList();
- bool loadGame(Common::String);
- bool saveGame(Common::String);
- void deleteSave(Common::String);
+ bool loadGame(const Common::String &);
+ bool saveGame(const Common::String &);
+ void deleteSave(const Common::String &);
void initMystVariables(MystVariables *_tv);
void debug_printMystVariables(MystVariables *_tv);
diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp
index a8cd643e2c..b67c109acc 100644
--- a/engines/mohawk/myst_scripts.cpp
+++ b/engines/mohawk/myst_scripts.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "mohawk/cursors.h"
#include "mohawk/myst.h"
#include "mohawk/graphics.h"
#include "mohawk/myst_scripts.h"
@@ -33,6 +34,19 @@
namespace Mohawk {
+MystScriptEntry::MystScriptEntry() {
+ type = kMystScriptNone;
+ var = 0;
+ argc = 0;
+ argv = 0;
+ u0 = 0;
+ u1 = 0;
+}
+
+MystScriptEntry::~MystScriptEntry() {
+ delete[] argv;
+}
+
const uint8 stack_map[8] = {
kSeleniticStack,
kStoneshipStack,
@@ -78,12 +92,12 @@ void MystScriptParser::setupOpcodes() {
OPCODE(2, altDest),
OPCODE(3, takePage),
OPCODE(4, opcode_4),
- // TODO: Opcode 5 Not Present
+ // Opcode 5 Not Present
OPCODE(6, opcode_6),
OPCODE(7, opcode_7),
OPCODE(8, opcode_8),
OPCODE(9, opcode_9),
- // TODO: Opcode 10 to 11 Not Present
+ // Opcode 10 to 11 Not Present
OPCODE(12, altDest),
OPCODE(13, altDest),
OPCODE(14, opcode_14),
@@ -97,7 +111,7 @@ void MystScriptParser::setupOpcodes() {
OPCODE(22, opcode_22),
OPCODE(23, opcode_23),
OPCODE(24, playSound),
- // TODO: Opcode 25 Not Present
+ // Opcode 25 Not Present
OPCODE(26, opcode_26),
OPCODE(27, playSoundBlocking),
OPCODE(28, opcode_28),
@@ -117,9 +131,9 @@ void MystScriptParser::setupOpcodes() {
OPCODE(42, opcode_42),
OPCODE(43, opcode_43),
OPCODE(44, opcode_44),
- // TODO: Opcode 45 Not Present
+ // Opcode 45 Not Present
OPCODE(46, opcode_46),
- // TODO: Opcodes 47 to 99 Not Present
+ // Opcodes 47 to 99 Not Present
// "Stack-Specific" Opcodes
OPCODE(100, opcode_100),
@@ -156,18 +170,18 @@ void MystScriptParser::setupOpcodes() {
OPCODE(131, opcode_131),
OPCODE(132, opcode_132),
OPCODE(133, opcode_133),
- // TODO: Opcodes 134 to 146 Not Present
+ // Opcodes 134 to 146 Not Present
OPCODE(147, opcode_147),
- // TODO: Opcodes 148 to 163 Not Present
+ // Opcodes 148 to 163 Not Present
OPCODE(164, opcode_164),
- // TODO: Opcodes 165 to 168 Not Present
+ // Opcodes 165 to 168 Not Present
OPCODE(169, opcode_169),
- // TODO: Opcodes 170 to 181 Not Present
+ // Opcodes 170 to 181 Not Present
OPCODE(182, opcode_182),
OPCODE(183, opcode_183),
OPCODE(184, opcode_184),
OPCODE(185, opcode_185),
- // TODO: Opcodes 186 to 195 Not Present
+ // Opcodes 186 to 195 Not Present
OPCODE(196, opcode_196), // Demo only
OPCODE(197, opcode_197), // Demo only
OPCODE(198, opcode_198),
@@ -197,7 +211,7 @@ void MystScriptParser::setupOpcodes() {
OPCODE(220, opcode_220),
OPCODE(221, opcode_221),
OPCODE(222, opcode_222),
- // TODO: Opcodes 223 to 297 Not Present
+ // Opcodes 223 to 297 Not Present
OPCODE(298, opcode_298), // Demo only
OPCODE(299, opcode_299), // Demo only
@@ -212,9 +226,9 @@ void MystScriptParser::setupOpcodes() {
OPCODE(307, opcode_307),
OPCODE(308, opcode_308),
OPCODE(309, opcode_309),
- // TODO: Opcodes 310 to 311 Not Present
+ // Opcodes 310 to 311 Not Present
OPCODE(312, opcode_312),
- // TODO: Opcodes 313 and greater Not Present
+ // Opcodes 313 and greater Not Present
OPCODE(0xFFFF, NOP)
};
@@ -251,13 +265,14 @@ void MystScriptParser::runPersistentOpcodes() {
opcode_212_run();
}
-void MystScriptParser::runScript(uint16 scriptCount, MystScriptEntry *scripts, MystResource *invokingResource) {
+void MystScriptParser::runScript(MystScript script, MystResource *invokingResource) {
_invokingResource = invokingResource;
- debugC(kDebugScript, "Script Count: %d", scriptCount);
- for (uint16 i = 0; i < scriptCount; i++) {
- debugC(kDebugScript, "\tOpcode %d: %d", i, scripts[i].opcode);
- runOpcode(scripts[i].opcode, scripts[i].var, scripts[i].numValues, scripts[i].values);
+ debugC(kDebugScript, "Script Size: %d", script->size());
+ for (uint16 i = 0; i < script->size(); i++) {
+ MystScriptEntry &entry = script->operator[](i);
+ debugC(kDebugScript, "\tOpcode %d: %d", i, entry.opcode);
+ runOpcode(entry.opcode, entry.var, entry.argc, entry.argv);
}
}
@@ -284,6 +299,41 @@ const char *MystScriptParser::getOpcodeDesc(uint16 op) {
return "";
}
+MystScript MystScriptParser::readScript(Common::SeekableReadStream *stream, MystScriptType type) {
+ assert(stream);
+ assert(type != kMystScriptNone);
+
+ MystScript script = MystScript(new Common::Array<MystScriptEntry>());
+
+ uint16 opcodeCount = stream->readUint16LE();
+ script->resize(opcodeCount);
+
+ for (uint16 i = 0; i < opcodeCount; i++) {
+ MystScriptEntry &entry = script->operator[](i);
+ entry.type = type;
+
+ // u0 only exists in INIT and EXIT scripts
+ if (type != kMystScriptNormal)
+ entry.u0 = stream->readUint16LE();
+
+ entry.opcode = stream->readUint16LE();
+ entry.var = stream->readUint16LE();
+ entry.argc = stream->readUint16LE();
+
+ if (entry.argc > 0) {
+ entry.argv = new uint16[entry.argc];
+ for (uint16 j = 0; j < entry.argc; j++)
+ entry.argv[j] = stream->readUint16LE();
+ }
+
+ // u1 exists only in EXIT scripts
+ if (type == kMystScriptExit)
+ entry.u1 = stream->readUint16LE();
+ }
+
+ return script;
+}
+
// 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,
@@ -294,17 +344,16 @@ void MystScriptParser::varUnusedCheck(uint16 op, uint16 var) {
}
void MystScriptParser::unknown(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- // NOTE: printf used here instead of debug, so unknown opcodes are always reported...
- printf("Unimplemented opcode 0x%02x (%d)\n", op, op);
- printf("\tUses var %d\n", var);
- printf("\tArg count = %d\n", argc);
- if (argc)
- printf("\tArgs: ");
- for (uint16 i = 0; i < argc; i++) {
- if (i == argc - 1)
- printf("%d\n", argv[i]);
- else
- printf("%d, ", argv[i]);
+ warning("Unimplemented opcode 0x%02x (%d)", op, op);
+ warning("\tUses var %d", var);
+ warning("\tArg count = %d", argc);
+ if (argc) {
+ Common::String str;
+ str += Common::String::format("%d", argv[0]);
+ for (uint16 i = 1; i < argc; i++) {
+ str += Common::String::format(", %d", argv[i]);
+ }
+ warning("\tArgs: %s\n", str.c_str());
}
}
@@ -382,9 +431,10 @@ void MystScriptParser::opcode_4(uint16 op, uint16 var, uint16 argc, uint16 *argv
// the general case, rather than this image blit...
uint16 var_value = _vm->_varStore->getVar(var);
if (var_value < _vm->_view.scriptResCount) {
- if (_vm->_view.scriptResources[var_value].type == 1) // TODO: Add Symbols for Types
+ if (_vm->_view.scriptResources[var_value].type == 1) { // TODO: Add Symbols for Types
_vm->_gfx->copyImageToScreen(_vm->_view.scriptResources[var_value].id, Common::Rect(0, 0, 544, 333));
- else
+ _vm->_gfx->updateScreen();
+ } else
warning("Opcode %d: Script Resource %d Type Not Image", op, var_value);
} else
warning("Opcode %d: var %d value %d outside Script Resource Range %d", op, var, var_value, _vm->_view.scriptResCount);
@@ -953,7 +1003,7 @@ void MystScriptParser::opcode_35(uint16 op, uint16 var, uint16 argc, uint16 *arg
debugC(kDebugScript, "\tdelay: %d", delay);
_vm->_gfx->copyImageToScreen(imageId, Common::Rect(0, 0, 544, 333));
- _vm->_system->updateScreen();
+ _vm->_gfx->updateScreen();
_vm->_system->delayMillis(delay * 100);
_vm->changeToCard(cardId);
} else
@@ -968,7 +1018,7 @@ void MystScriptParser::changeCursor(uint16 op, uint16 var, uint16 argc, uint16 *
debugC(kDebugScript, "Cursor: %d", argv[0]);
// TODO: Not sure if this needs to change mainCursor or similar...
- _vm->_gfx->changeCursor(argv[0]);
+ _vm->_cursor->setCursor(argv[0]);
} else
unknown(op, var, argc, argv);
}
@@ -978,7 +1028,7 @@ void MystScriptParser::hideCursor(uint16 op, uint16 var, uint16 argc, uint16 *ar
if (argc == 0) {
debugC(kDebugScript, "Opcode %d: Hide Cursor", op);
- _vm->_gfx->hideCursor();
+ _vm->_cursor->hideCursor();
} else
unknown(op, var, argc, argv);
}
@@ -988,7 +1038,7 @@ void MystScriptParser::showCursor(uint16 op, uint16 var, uint16 argc, uint16 *ar
if (argc == 0) {
debugC(kDebugScript, "Opcode %d: Show Cursor", op);
- _vm->_gfx->showCursor();
+ _vm->_cursor->showCursor();
} else
unknown(op, var, argc, argv);
}
@@ -1529,7 +1579,7 @@ void MystScriptParser::opcode_102(uint16 op, uint16 var, uint16 argc, uint16 *ar
debugC(kDebugScript, "\tstartTime: %d", startTime);
debugC(kDebugScript, "\tendTime: %d", endTime);
- printf("TODO: Opcode %d Movie Time Index %d to %d\n", op, startTime, endTime);
+ warning("TODO: Opcode %d Movie Time Index %d to %d", op, startTime, endTime);
// TODO: Need version of playMovie blocking which allows selection
// of start and finish points.
_vm->_video->playMovie(_vm->wrapMovieFilename("bkroom", kStoneshipStack), 159, 99);
@@ -1686,6 +1736,7 @@ void MystScriptParser::opcode_104(uint16 op, uint16 var, uint16 argc, uint16 *ar
// TODO: Need to load the image ids from Script Resources structure of VIEW
for (uint16 imageId = 3595; imageId <= 3601; imageId++) {
_vm->_gfx->copyImageToScreen(imageId, rect);
+ _vm->_gfx->updateScreen();
_vm->_system->delayMillis(50);
}
@@ -2488,7 +2539,7 @@ void MystScriptParser::opcode_121(uint16 op, uint16 var, uint16 argc, uint16 *ar
uint16 startTime = argv[0];
uint16 endTime = argv[1];
- printf("TODO: Opcode %d Movie Time Index %d to %d\n", op, startTime, endTime);
+ warning("TODO: Opcode %d Movie Time Index %d to %d\n", op, startTime, endTime);
// TODO: Need version of playMovie blocking which allows selection
// of start and finish points.
_vm->_video->playMovie(_vm->wrapMovieFilename("ewindow", kMechanicalStack), 253, 0);
@@ -2516,6 +2567,7 @@ void MystScriptParser::opcode_122(uint16 op, uint16 var, uint16 argc, uint16 *ar
// TODO: Need to load the image ids from Script Resources structure of VIEW
for (uint16 imageId = 3601; imageId >= 3595; imageId--) {
_vm->_gfx->copyImageToScreen(imageId, rect);
+ _vm->_gfx->updateScreen();
_vm->_system->delayMillis(50);
}
@@ -2550,7 +2602,7 @@ void MystScriptParser::opcode_123(uint16 op, uint16 var, uint16 argc, uint16 *ar
uint16 start_time = argv[0];
uint16 end_time = argv[1];
- printf("TODO: Opcode %d Movie Time Index %d to %d\n", op, start_time, end_time);
+ warning("TODO: Opcode %d Movie Time Index %d to %d\n", op, start_time, end_time);
// TODO: Need version of playMovie blocking which allows selection
// of start and finish points.
// TODO: Not 100% sure about movie position
@@ -3143,8 +3195,10 @@ void MystScriptParser::opcode_200_run() {
else
rect = Common::Rect(0, 0, 544, 333);
- if (curImageIndex != lastImageIndex)
+ if (curImageIndex != lastImageIndex) {
_vm->_gfx->copyImageToScreen(g_opcode200Parameters.imageBaseId + curImageIndex, rect);
+ _vm->_gfx->updateScreen();
+ }
// TODO: Comparison with original engine shows that this simple solution
// may not be the correct one and the choice of which sound
@@ -3166,6 +3220,7 @@ void MystScriptParser::opcode_200_run() {
// Note: The modulus by 6 is because the 6th image is the one at imageBaseId
_vm->_gfx->copyImageToScreen(g_opcode200Parameters.imageBaseId + curImageIndex % 6, Common::Rect(0, 0, 544, 333));
+ _vm->_gfx->updateScreen();
_vm->_varStore->setVar(g_opcode200Parameters.var, curImageIndex + 1);
g_opcode200Parameters.lastCardTime = _vm->_system->getMillis();
@@ -3380,6 +3435,7 @@ void MystScriptParser::opcode_201(uint16 op, uint16 var, uint16 argc, uint16 *ar
case kIntroStack:
_vm->_system->delayMillis(4 * 1000);
_vm->_gfx->copyImageToScreen(4, Common::Rect(0, 0, 544, 333));
+ _vm->_gfx->updateScreen();
// TODO : Wait until video ends, then change to card 5
break;
case kSeleniticStack:
@@ -4328,6 +4384,8 @@ void MystScriptParser::opcode_211_run(void) {
lastGridState[i] = gridState[i];
}
+ _vm->_gfx->updateScreen();
+
// Var 23 contains boolean for whether pattern matches correct book pattern i.e. Pattern 158
if (gridState[0] == 0xc3 && gridState[1] == 0x6b && gridState[2] == 0xa3 &&
gridState[3] == 0x93 && gridState[4] == 0xcc && gridState[5] == 0xfa)
@@ -4657,6 +4715,7 @@ void MystScriptParser::opcode_298(uint16 op, uint16 var, uint16 argc, uint16 *ar
_vm->_system->delayMillis(20 * 1000);
for (uint16 imageId = 3001; imageId <= 3012; imageId++) {
_vm->_gfx->copyImageToScreen(imageId, Common::Rect(0, 0, 544, 333));
+ _vm->_gfx->updateScreen();
_vm->_system->delayMillis(5 * 1000);
}
break;
diff --git a/engines/mohawk/myst_scripts.h b/engines/mohawk/myst_scripts.h
index a996421289..af8053bacf 100644
--- a/engines/mohawk/myst_scripts.h
+++ b/engines/mohawk/myst_scripts.h
@@ -26,6 +26,7 @@
#ifndef MYST_SCRIPTS_H
#define MYST_SCRIPTS_H
+#include "common/ptr.h"
#include "common/scummsys.h"
#include "common/util.h"
@@ -34,16 +35,39 @@ namespace Mohawk {
#define DECLARE_OPCODE(x) void x(uint16 op, uint16 var, uint16 argc, uint16 *argv)
class MohawkEngine_Myst;
-struct MystScriptEntry;
+class MystResource;
+
+enum MystScriptType {
+ kMystScriptNone,
+ kMystScriptNormal,
+ kMystScriptInit,
+ kMystScriptExit
+};
+
+struct MystScriptEntry {
+ MystScriptEntry();
+ ~MystScriptEntry();
+
+ MystScriptType type;
+ uint16 u0;
+ uint16 opcode;
+ uint16 var;
+ uint16 argc;
+ uint16 *argv;
+ uint16 u1;
+};
+
+typedef Common::SharedPtr<Common::Array<MystScriptEntry> > MystScript;
class MystScriptParser {
public:
MystScriptParser(MohawkEngine_Myst *vm);
~MystScriptParser();
- void runScript(uint16 scriptCount, MystScriptEntry *scripts, MystResource* invokingResource = NULL);
+ void runScript(MystScript script, MystResource *invokingResource = NULL);
void runOpcode(uint16 op, uint16 var = 0, uint16 argc = 0, uint16 *argv = NULL);
const char *getOpcodeDesc(uint16 op);
+ MystScript readScript(Common::SeekableReadStream *stream, MystScriptType type);
void disableInitOpcodes();
void runPersistentOpcodes();
diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp
index 74efd6770f..ee82bb4cea 100644
--- a/engines/mohawk/resource.cpp
+++ b/engines/mohawk/resource.cpp
@@ -25,6 +25,7 @@
#include "mohawk/resource.h"
+#include "common/substream.h"
#include "common/util.h"
namespace Mohawk {
@@ -35,14 +36,22 @@ MohawkArchive::MohawkArchive() {
_fileTable = NULL;
}
-void MohawkArchive::open(Common::String filename) {
+bool MohawkArchive::open(const Common::String &filename) {
Common::File *file = new Common::File();
- if (!file->open(filename.c_str()))
- error ("Could not open file \'%s\'", filename.c_str());
+
+ if (!file->open(filename)) {
+ delete file;
+ return false;
+ }
_curFile = filename;
- open(file);
+ if (!open(file)) {
+ close();
+ return false;
+ }
+
+ return true;
}
void MohawkArchive::close() {
@@ -53,23 +62,29 @@ void MohawkArchive::close() {
_curFile.clear();
}
-void MohawkArchive::open(Common::SeekableReadStream *stream) {
+bool MohawkArchive::open(Common::SeekableReadStream *stream) {
// Make sure no other file is open...
close();
_mhk = stream;
- if (_mhk->readUint32BE() != ID_MHWK)
- error ("Could not find tag \'MHWK\'");
+ if (_mhk->readUint32BE() != ID_MHWK) {
+ warning("Could not find tag 'MHWK'");
+ return false;
+ }
- _fileSize = _mhk->readUint32BE();
+ /* uint32 fileSize = */ _mhk->readUint32BE();
- if (_mhk->readUint32BE() != ID_RSRC)
- error ("Could not find tag \'RSRC\'");
+ if (_mhk->readUint32BE() != ID_RSRC) {
+ warning("Could not find tag \'RSRC\'");
+ return false;
+ }
_rsrc.version = _mhk->readUint16BE();
- if (_rsrc.version != 0x100)
- error("Unsupported Mohawk resource version %d.%d", (_rsrc.version >> 8) & 0xff, _rsrc.version & 0xff);
+ if (_rsrc.version != 0x100) {
+ warning("Unsupported Mohawk resource version %d.%d", (_rsrc.version >> 8) & 0xff, _rsrc.version & 0xff);
+ return false;
+ }
_rsrc.compaction = _mhk->readUint16BE(); // Only used in creation, not in reading
_rsrc.filesize = _mhk->readUint32BE();
@@ -170,6 +185,55 @@ void MohawkArchive::open(Common::SeekableReadStream *stream) {
debug (4, "File[%02x]: Offset = %08x DataSize = %07x Flags = %02x Unk = %04x", i, _fileTable[i].offset, _fileTable[i].dataSize, _fileTable[i].flags, _fileTable[i].unk);
}
+
+ return true;
+}
+
+int MohawkArchive::getTypeIndex(uint32 tag) {
+ for (uint16 i = 0; i < _typeTable.resource_types; i++)
+ if (_types[i].tag == tag)
+ return i;
+ return -1; // not found
+}
+
+int MohawkArchive::getIDIndex(int typeIndex, uint16 id) {
+ for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
+ if (_types[typeIndex].resTable.entries[i].id == id)
+ return i;
+ return -1; // not found
+}
+
+int MohawkArchive::getIDIndex(int typeIndex, const Common::String &resName) {
+ int index = -1;
+
+ for (uint16 i = 0; i < _types[typeIndex].nameTable.num; i++)
+ if (_types[typeIndex].nameTable.entries[i].name.matchString(resName)) {
+ index = _types[typeIndex].nameTable.entries[i].index;
+ break;
+ }
+
+ if (index < 0)
+ return -1; // Not found
+
+ for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
+ if (_types[typeIndex].resTable.entries[i].index == index)
+ return i;
+
+ return -1; // Not found
+}
+
+uint16 MohawkArchive::findResourceID(uint32 type, const Common::String &resName) {
+ int typeIndex = getTypeIndex(type);
+
+ if (typeIndex < 0)
+ return 0xFFFF;
+
+ int idIndex = getIDIndex(typeIndex, resName);
+
+ if (idIndex < 0)
+ return 0xFFFF;
+
+ return _types[typeIndex].resTable.entries[idIndex].id;
}
bool MohawkArchive::hasResource(uint32 tag, uint16 id) {
@@ -181,7 +245,19 @@ bool MohawkArchive::hasResource(uint32 tag, uint16 id) {
if (typeIndex < 0)
return false;
- return (getIdIndex(typeIndex, id) >= 0);
+ return getIDIndex(typeIndex, id) >= 0;
+}
+
+bool MohawkArchive::hasResource(uint32 tag, const Common::String &resName) {
+ if (!_mhk)
+ return false;
+
+ int16 typeIndex = getTypeIndex(tag);
+
+ if (typeIndex < 0)
+ return false;
+
+ return getIDIndex(typeIndex, resName) >= 0;
}
uint32 MohawkArchive::getOffset(uint32 tag, uint16 id) {
@@ -190,25 +266,25 @@ uint32 MohawkArchive::getOffset(uint32 tag, uint16 id) {
int16 typeIndex = getTypeIndex(tag);
assert(typeIndex >= 0);
- int16 idIndex = getIdIndex(typeIndex, id);
+ int16 idIndex = getIDIndex(typeIndex, id);
assert(idIndex >= 0);
return _fileTable[_types[typeIndex].resTable.entries[idIndex].index - 1].offset;
}
-Common::SeekableReadStream *MohawkArchive::getRawData(uint32 tag, uint16 id) {
+Common::SeekableReadStream *MohawkArchive::getResource(uint32 tag, uint16 id) {
if (!_mhk)
- error ("MohawkArchive::getRawData - No File in Use");
+ error("MohawkArchive::getResource(): No File in Use");
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
- error ("Could not find a tag of \'%s\' in file \'%s\'", tag2str(tag), _curFile.c_str());
+ error("Could not find a tag of '%s' in file '%s'", tag2str(tag), _curFile.c_str());
- int16 idIndex = getIdIndex(typeIndex, id);
+ int16 idIndex = getIDIndex(typeIndex, id);
if (idIndex < 0)
- error ("Could not find \'%s\' %04x in file \'%s\'", tag2str(tag), id, _curFile.c_str());
+ error("Could not find '%s' %04x in file '%s'", tag2str(tag), id, _curFile.c_str());
// Note: the fileTableIndex is based off 1, not 0. So, subtract 1
uint16 fileTableIndex = _types[typeIndex].resTable.entries[idIndex].index - 1;
@@ -227,7 +303,7 @@ Common::SeekableReadStream *MohawkArchive::getRawData(uint32 tag, uint16 id) {
return new Common::SeekableSubReadStream(_mhk, _fileTable[fileTableIndex].offset, _fileTable[fileTableIndex].offset + _fileTable[fileTableIndex].dataSize);
}
-void LivingBooksArchive_v1::open(Common::SeekableReadStream *stream) {
+bool LivingBooksArchive_v1::open(Common::SeekableReadStream *stream) {
close();
_mhk = stream;
@@ -299,8 +375,8 @@ void LivingBooksArchive_v1::open(Common::SeekableReadStream *stream) {
for (uint16 j = 0; j < _types[i].resTable.resources; j++) {
_types[i].resTable.entries[j].id = _mhk->readUint16LE();
_types[i].resTable.entries[j].offset = _mhk->readUint32LE();
- _types[i].resTable.entries[j].size = _mhk->readUint16LE();
- _mhk->readUint32LE(); // Unknown (always 0?)
+ _types[i].resTable.entries[j].size = _mhk->readUint32LE();
+ _mhk->readUint16LE(); // Unknown (always 0?)
debug (4, "Entry[%02x]: ID = %04x (%d)\tOffset = %08x, Size = %08x", j, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].id, _types[i].resTable.entries[j].offset, _types[i].resTable.entries[j].size);
}
@@ -308,9 +384,12 @@ void LivingBooksArchive_v1::open(Common::SeekableReadStream *stream) {
_mhk->seek(oldPos);
debug (3, "\n");
}
- } else
- error("Could not determine type of Old Mohawk resource");
+ } else {
+ warning("Could not determine type of Old Mohawk resource");
+ return false;
+ }
+ return true;
}
uint32 LivingBooksArchive_v1::getOffset(uint32 tag, uint16 id) {
@@ -319,27 +398,53 @@ uint32 LivingBooksArchive_v1::getOffset(uint32 tag, uint16 id) {
int16 typeIndex = getTypeIndex(tag);
assert(typeIndex >= 0);
- int16 idIndex = getIdIndex(typeIndex, id);
+ int16 idIndex = getIDIndex(typeIndex, id);
assert(idIndex >= 0);
return _types[typeIndex].resTable.entries[idIndex].offset;
}
-Common::SeekableReadStream *LivingBooksArchive_v1::getRawData(uint32 tag, uint16 id) {
+Common::SeekableReadStream *LivingBooksArchive_v1::getResource(uint32 tag, uint16 id) {
if (!_mhk)
- error ("LivingBooksArchive_v1::getRawData - No File in Use");
+ error("LivingBooksArchive_v1::getResource(): No File in Use");
int16 typeIndex = getTypeIndex(tag);
if (typeIndex < 0)
- error ("Could not find a tag of \'%s\' in file \'%s\'", tag2str(tag), _curFile.c_str());
+ error("Could not find a tag of \'%s\' in file \'%s\'", tag2str(tag), _curFile.c_str());
- int16 idIndex = getIdIndex(typeIndex, id);
+ int16 idIndex = getIDIndex(typeIndex, id);
if (idIndex < 0)
- error ("Could not find \'%s\' %04x in file \'%s\'", tag2str(tag), id, _curFile.c_str());
+ error("Could not find \'%s\' %04x in file \'%s\'", tag2str(tag), id, _curFile.c_str());
return new Common::SeekableSubReadStream(_mhk, _types[typeIndex].resTable.entries[idIndex].offset, _types[typeIndex].resTable.entries[idIndex].offset + _types[typeIndex].resTable.entries[idIndex].size);
}
+bool LivingBooksArchive_v1::hasResource(uint32 tag, uint16 id) {
+ if (!_mhk)
+ return false;
+
+ int16 typeIndex = getTypeIndex(tag);
+
+ if (typeIndex < 0)
+ return false;
+
+ return getIDIndex(typeIndex, id) >= 0;
+}
+
+int LivingBooksArchive_v1::getTypeIndex(uint32 tag) {
+ for (uint16 i = 0; i < _typeTable.resource_types; i++)
+ if (_types[i].tag == tag)
+ return i;
+ return -1; // not found
+}
+
+int LivingBooksArchive_v1::getIDIndex(int typeIndex, uint16 id) {
+ for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
+ if (_types[typeIndex].resTable.entries[i].id == id)
+ return i;
+ return -1; // not found
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/resource.h b/engines/mohawk/resource.h
index bd63ae7f44..675c8f3f8c 100644
--- a/engines/mohawk/resource.h
+++ b/engines/mohawk/resource.h
@@ -42,7 +42,7 @@ namespace Mohawk {
// Myst Resource FourCC's
#define ID_CLRC MKID_BE('CLRC') // Cursor Hotspots
#define ID_EXIT MKID_BE('EXIT') // Card Exit Scripts
-#define ID_HINT MKID_BE('HINT') // Specifies Cursors in What Area
+#define ID_HINT MKID_BE('HINT') // Cursor Hints
#define ID_INIT MKID_BE('INIT') // Card Entrance Scripts
#define ID_MSND MKID_BE('MSND') // Standard Mohawk Sound
#define ID_RLST MKID_BE('RLST') // Resource List, Specifies HotSpots
@@ -53,7 +53,7 @@ namespace Mohawk {
// Myst Masterpiece Edition Resource FourCC's (In addition to Myst FourCC's)
#define ID_HELP MKID_BE('HELP') // Help Chunk
#define ID_MJMP MKID_BE('MJMP') // MSND Jumps (To reduce MSND duplication)
-#define ID_PICT MKID_BE('PICT') // JPEG/PICT Image
+#define ID_PICT MKID_BE('PICT') // JPEG/PICT/WDIB Image
// Riven Resource FourCC's
#define ID_BLST MKID_BE('BLST') // Card Hotspot Enabling Lists
@@ -63,34 +63,36 @@ namespace Mohawk {
#define ID_MLST MKID_BE('MLST') // Card Movie Lists
#define ID_NAME MKID_BE('NAME') // Object Names
#define ID_PLST MKID_BE('PLST') // Card Picture Lists
-#define ID_RMAP MKID_BE('RMAP') // Card Code
+#define ID_RMAP MKID_BE('RMAP') // Card Codes
#define ID_SFXE MKID_BE('SFXE') // Water Effect Animations
#define ID_SLST MKID_BE('SLST') // Card Ambient Sound Lists
-#define ID_TMOV MKID_BE('tMOV') // Game Movie
+#define ID_TMOV MKID_BE('tMOV') // QuickTime Movie
// Riven Saved Game FourCC's
-#define ID_VARS MKID_BE('VARS') // Saved Game Variable Values
+#define ID_VARS MKID_BE('VARS') // Variable Values
#define ID_VERS MKID_BE('VERS') // Version Info
#define ID_ZIPS MKID_BE('ZIPS') // Zip Mode Status
// Zoombini Resource FourCC's
#define ID_SND MKID_BE('\0SND') // Standard Mohawk Sound
#define ID_CURS MKID_BE('CURS') // Cursor?
-#define ID_SCRB MKID_BE('SCRB') // ???
-#define ID_SCRS MKID_BE('SCRS') // ???
-#define ID_NODE MKID_BE('NODE') // ???
-#define ID_PATH MKID_BE('PATH') // ???
-#define ID_SHPL MKID_BE('SHPL') // ???
+#define ID_SCRB MKID_BE('SCRB') // Script?
+#define ID_SCRS MKID_BE('SCRS') // Script?
+#define ID_NODE MKID_BE('NODE') // Walk Node?
+#define ID_PATH MKID_BE('PATH') // Walk Path?
+#define ID_SHPL MKID_BE('SHPL') // Shape List?
// Living Books Resource FourCC's
#define ID_TCUR MKID_BE('tCUR') // Cursor
-#define ID_BITL MKID_BE('BITL') // ???
-#define ID_CTBL MKID_BE('CTBL') // Color Table?
-#define ID_SCRP MKID_BE('SCRP') // Script?
-#define ID_SPR MKID_BE('SPR#') // Sprites?
+#define ID_BITL MKID_BE('BITL') // Book Item List
+#define ID_CTBL MKID_BE('CTBL') // Color Table
+#define ID_SCRP MKID_BE('SCRP') // Script
+#define ID_SPR MKID_BE('SPR#') // Sprite?
#define ID_VRSN MKID_BE('VRSN') // Version
-#define ID_ANI MKID_BE('ANI ') // Animation?
-#define ID_SHP MKID_BE('SHP#') // ???
+#define ID_ANI MKID_BE('ANI ') // Animation
+#define ID_SHP MKID_BE('SHP#') // Shape
+#define ID_WAV MKID_BE('WAV ') // Old Sound Resource
+#define ID_BMAP MKID_BE('BMAP') // Old Mohawk Bitmap
// JamesMath Resource FourCC's
#define ID_TANM MKID_BE('tANM') // Animation?
@@ -104,11 +106,7 @@ namespace Mohawk {
// Mohawk MIDI Tags
#define ID_MIDI MKID_BE('MIDI') // Game Sound (Third Tag), instead of WAVE
-#define ID_PRG MKID_BE('Prg#') // Midi Program?
-
-// Old Mohawk Resource FourCC's
-#define ID_WAV MKID_BE('WAV ') // Old Sound Resource
-#define ID_BMAP MKID_BE('BMAP') // Standard Mohawk Bitmap
+#define ID_PRG MKID_BE('Prg#') // MIDI Patch
// Common Resource FourCC's
#define ID_TBMP MKID_BE('tBMP') // Standard Mohawk Bitmap
@@ -120,8 +118,8 @@ namespace Mohawk {
#define ID_TBMH MKID_BE('tBMH') // Standard Mohawk Bitmap
#define ID_TMID MKID_BE('tMID') // Standard Mohawk MIDI
#define ID_REGS MKID_BE('REGS') // ??? (Zoombini, Treehouse)
-#define ID_BYTS MKID_BE('BYTS') // Database Entry (CSWorld, CSAmtrak)
-#define ID_INTS MKID_BE('INTS') // ??? (CSWorld, CSAmtrak)
+#define ID_BYTS MKID_BE('BYTS') // Byte Array? (Used as Database Entry in CSWorld, CSAmtrak)
+#define ID_INTS MKID_BE('INTS') // uint16 Array? (CSWorld, CSAmtrak)
#define ID_BBOX MKID_BE('BBOX') // Boxes? (CSWorld, CSAmtrak)
#define ID_SYSX MKID_BE('SYSX') // MIDI Sysex
@@ -179,13 +177,15 @@ public:
MohawkArchive();
virtual ~MohawkArchive() { close(); }
- void open(Common::String filename);
- virtual void open(Common::SeekableReadStream *stream);
+ bool open(const Common::String &filename);
+ virtual bool open(Common::SeekableReadStream *stream);
void close();
- bool hasResource(uint32 tag, uint16 id);
- virtual Common::SeekableReadStream *getRawData(uint32 tag, uint16 id);
+ virtual bool hasResource(uint32 tag, uint16 id);
+ virtual bool hasResource(uint32 tag, const Common::String &resName);
+ virtual Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
virtual uint32 getOffset(uint32 tag, uint16 id);
+ virtual uint16 findResourceID(uint32 type, const Common::String &resName);
protected:
Common::SeekableReadStream *_mhk;
@@ -193,8 +193,6 @@ protected:
Common::String _curFile;
private:
- bool _hasData;
- uint32 _fileSize;
RSRC_Header _rsrc;
Type *_types;
FileTable *_fileTable;
@@ -202,19 +200,9 @@ private:
uint16 _resourceTableAmount;
uint16 _fileTableAmount;
- virtual int16 getTypeIndex(uint32 tag) {
- for (uint16 i = 0; i < _typeTable.resource_types; i++)
- if (_types[i].tag == tag)
- return i;
- return -1; // not found
- }
-
- virtual int16 getIdIndex(int16 typeIndex, uint16 id) {
- for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
- if (_types[typeIndex].resTable.entries[i].id == id)
- return i;
- return -1; // not found
- }
+ int getTypeIndex(uint32 tag);
+ int getIDIndex(int typeIndex, uint16 id);
+ int getIDIndex(int typeIndex, const Common::String &resName);
};
class LivingBooksArchive_v1 : public MohawkArchive {
@@ -222,9 +210,13 @@ public:
LivingBooksArchive_v1() : MohawkArchive() {}
~LivingBooksArchive_v1() {}
- void open(Common::SeekableReadStream *stream);
- Common::SeekableReadStream *getRawData(uint32 tag, uint16 id);
+ bool hasResource(uint32 tag, uint16 id);
+ bool hasResource(uint32 tag, const Common::String &resName) { return false; }
+ bool open(Common::SeekableReadStream *stream);
+ Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
+ Common::SeekableReadStream *getResource(uint32 tag, const Common::String &resName) { return 0; }
uint32 getOffset(uint32 tag, uint16 id);
+ uint16 findResourceID(uint32 type, const Common::String &resName) { return 0xFFFF; }
private:
struct OldType {
@@ -240,19 +232,8 @@ private:
} resTable;
} *_types;
- int16 getTypeIndex(uint32 tag) {
- for (uint16 i = 0; i < _typeTable.resource_types; i++)
- if (_types[i].tag == tag)
- return i;
- return -1; // not found
- }
-
- int16 getIdIndex(int16 typeIndex, uint16 id) {
- for (uint16 i = 0; i < _types[typeIndex].resTable.resources; i++)
- if (_types[typeIndex].resTable.entries[i].id == id)
- return i;
- return -1; // not found
- }
+ int getTypeIndex(uint32 tag);
+ int getIDIndex(int typeIndex, uint16 id);
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/resource_cache.cpp b/engines/mohawk/resource_cache.cpp
index 8525bbc127..75f608f5fd 100644
--- a/engines/mohawk/resource_cache.cpp
+++ b/engines/mohawk/resource_cache.cpp
@@ -43,16 +43,17 @@ void ResourceCache::clear() {
debugC(kDebugCache, "Clearing Cache...");
- // TODO: Not sure if need to explicitly delete DataObject.data element ie.
- // returned by readStream method here.
- store.clear();
+ for (uint32 i = 0; i < _store.size(); i++)
+ delete _store[i].data;
+
+ _store.clear();
}
void ResourceCache::add(uint32 tag, uint16 id, Common::SeekableReadStream *data) {
if (!enabled)
return;
- debugC(kDebugCache, "Adding item %d - tag 0x%04X id %d", store.size(), tag, id);
+ debugC(kDebugCache, "Adding item %d - tag 0x%04X id %d", _store.size(), tag, id);
DataObject current;
current.tag = tag;
@@ -60,7 +61,7 @@ void ResourceCache::add(uint32 tag, uint16 id, Common::SeekableReadStream *data)
uint32 dataCurPos = data->pos();
current.data = data->readStream(data->size());
data->seek(dataCurPos);
- store.push_back(current);
+ _store.push_back(current);
}
// Returns NULL if not found
@@ -70,12 +71,12 @@ Common::SeekableReadStream *ResourceCache::search(uint32 tag, uint16 id) {
debugC(kDebugCache, "Searching for tag 0x%04X id %d", tag, id);
- for (uint32 i = 0; i < store.size(); i++) {
- if (tag == store[i].tag && id == store[i].id) {
+ for (uint32 i = 0; i < _store.size(); i++) {
+ if (tag == _store[i].tag && id == _store[i].id) {
debugC(kDebugCache, "Found cached tag 0x%04X id %u", tag, id);
- uint32 dataCurPos = store[i].data->pos();
- Common::SeekableReadStream *ret = store[i].data->readStream(store[i].data->size());
- store[i].data->seek(dataCurPos);
+ uint32 dataCurPos = _store[i].data->pos();
+ Common::SeekableReadStream *ret = _store[i].data->readStream(_store[i].data->size());
+ _store[i].data->seek(dataCurPos);
return ret;
}
}
diff --git a/engines/mohawk/resource_cache.h b/engines/mohawk/resource_cache.h
index 506d223ff1..32e345cf4b 100644
--- a/engines/mohawk/resource_cache.h
+++ b/engines/mohawk/resource_cache.h
@@ -51,7 +51,7 @@ private:
Common::SeekableReadStream *data;
};
- Common::Array<DataObject> store;
+ Common::Array<DataObject> _store;
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index f2f4a42f0e..45b000ec5f 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -29,6 +29,7 @@
#include "common/keyboard.h"
#include "common/translation.h"
+#include "mohawk/cursors.h"
#include "mohawk/graphics.h"
#include "mohawk/resource.h"
#include "mohawk/riven.h"
@@ -111,6 +112,7 @@ Common::Error MohawkEngine_Riven::run() {
_externalScriptHandler = new RivenExternal(this);
_optionsDialog = new RivenOptionsDialog(this);
_scriptMan = new RivenScriptManager(this);
+ _cursor = new RivenCursorManager();
_rnd = new Common::RandomSource();
g_eventRec.registerRandomSource(*_rnd, "riven");
@@ -119,10 +121,12 @@ Common::Error MohawkEngine_Riven::run() {
// Open extras.mhk for common images
_extrasFile = new MohawkArchive();
- _extrasFile->open("extras.mhk");
+
+ if (!_extrasFile->open("extras.mhk"))
+ error("Could not open extras.mhk");
// Start at main cursor
- _gfx->changeCursor(kRivenMainCursor);
+ _cursor->setCursor(kRivenMainCursor);
// Let's begin, shall we?
if (getFeatures() & GF_DEMO) {
@@ -264,6 +268,9 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
_video->stopVideos();
_video->clearMLST();
+ // Clear the graphics cache; images aren't used across stack boundaries
+ _gfx->clearCache();
+
// Clear the old stack files out
for (uint32 i = 0; i < _mhk.size(); i++)
delete _mhk[i];
@@ -275,11 +282,12 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
// Load any file that fits the patterns
for (int i = 0; i < ARRAYSIZE(endings); i++) {
Common::String filename = Common::String(prefix) + endings[i];
- if (Common::File::exists(filename)) {
- MohawkArchive *mhk = new MohawkArchive();
- mhk->open(filename);
+
+ MohawkArchive *mhk = new MohawkArchive();
+ if (mhk->open(filename))
_mhk.push_back(mhk);
- }
+ else
+ delete mhk;
}
// Make sure we have loaded files
@@ -317,6 +325,10 @@ void MohawkEngine_Riven::changeToCard(uint16 dest) {
_curCard = dest;
debug (1, "Changing to card %d", _curCard);
+ // Clear the graphics cache (images typically aren't used
+ // on different cards).
+ _gfx->clearCache();
+
if (!(getFeatures() & GF_DEMO)) {
for (byte i = 0; i < 13; i++)
if (_curStack == rivenSpecialChange[i].startStack && _curCard == matchRMAPToCard(rivenSpecialChange[i].startCardRMAP)) {
@@ -363,7 +375,7 @@ void MohawkEngine_Riven::refreshCard() {
void MohawkEngine_Riven::loadCard(uint16 id) {
// NOTE: The card scripts are cleared by the RivenScriptManager automatically.
- Common::SeekableReadStream* inStream = getRawData(ID_CARD, id);
+ Common::SeekableReadStream* inStream = getResource(ID_CARD, id);
_cardData.name = inStream->readSint16BE();
_cardData.zipModePlace = inStream->readUint16BE();
@@ -390,7 +402,7 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
// NOTE: The hotspot scripts are cleared by the RivenScriptManager automatically.
- Common::SeekableReadStream *inStream = getRawData(ID_HSPT, id);
+ Common::SeekableReadStream *inStream = getResource(ID_HSPT, id);
_hotspotCount = inStream->readUint16BE();
_hotspots = new RivenHotspot[_hotspotCount];
@@ -468,11 +480,11 @@ void MohawkEngine_Riven::checkHotspotChange() {
if (foundHotspot) {
if (_curHotspot != hotspotIndex) {
_curHotspot = hotspotIndex;
- _gfx->changeCursor(_hotspots[_curHotspot].mouse_cursor);
+ _cursor->setCursor(_hotspots[_curHotspot].mouse_cursor);
}
} else {
_curHotspot = -1;
- _gfx->changeCursor(kRivenMainCursor);
+ _cursor->setCursor(kRivenMainCursor);
}
}
@@ -559,11 +571,11 @@ void MohawkEngine_Riven::checkInventoryClick() {
}
Common::SeekableReadStream *MohawkEngine_Riven::getExtrasResource(uint32 tag, uint16 id) {
- return _extrasFile->getRawData(tag, id);
+ return _extrasFile->getResource(tag, id);
}
Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) {
- Common::SeekableReadStream* nameStream = getRawData(ID_NAME, nameResource);
+ Common::SeekableReadStream* nameStream = getResource(ID_NAME, nameResource);
uint16 fieldCount = nameStream->readUint16BE();
uint16* stringOffsets = new uint16[fieldCount];
Common::String name;
@@ -591,7 +603,7 @@ Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) {
uint16 MohawkEngine_Riven::matchRMAPToCard(uint32 rmapCode) {
uint16 index = 0;
- Common::SeekableReadStream *rmapStream = getRawData(ID_RMAP, 1);
+ Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
for (uint16 i = 1; rmapStream->pos() < rmapStream->size(); i++) {
uint32 code = rmapStream->readUint32BE();
@@ -608,7 +620,7 @@ uint16 MohawkEngine_Riven::matchRMAPToCard(uint32 rmapCode) {
}
uint32 MohawkEngine_Riven::getCurCardRMAP() {
- Common::SeekableReadStream *rmapStream = getRawData(ID_RMAP, 1);
+ Common::SeekableReadStream *rmapStream = getResource(ID_RMAP, 1);
rmapStream->seek(_curCard * 4);
uint32 rmapCode = rmapStream->readUint32BE();
delete rmapStream;
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 21464a6a48..8848716967 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "mohawk/cursors.h"
#include "mohawk/graphics.h"
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
@@ -70,6 +71,8 @@ void RivenExternal::setupCommands() {
COMMAND(xalaunchbrowser);
COMMAND(xadisablemenuintro);
COMMAND(xaenablemenuintro);
+ COMMAND(xademoquit);
+ COMMAND(xaexittomain);
// bspit (Bookmaking Island) external commands
COMMAND(xblabopenbook);
@@ -257,7 +260,7 @@ void RivenExternal::runDomeCheck() {
*_vm->getVar("domecheck") = 1;
}
-void RivenExternal::resetDomeSliders(uint16 bitmapId, uint16 soundId, uint16 startHotspot) {
+void RivenExternal::resetDomeSliders(uint16 soundId, uint16 startHotspot) {
// The rightmost slider should move left until it finds the next slider,
// then those two continue until they find the third slider. This continues
// until all five sliders have returned their starting slots.
@@ -278,8 +281,8 @@ void RivenExternal::resetDomeSliders(uint16 bitmapId, uint16 soundId, uint16 sta
// so we should redraw and play a tick sound
if (slidersFound) {
_vm->_sound->playSound(soundId);
- drawDomeSliders(bitmapId, startHotspot);
- _vm->_system->delayMillis(10);
+ drawDomeSliders(startHotspot);
+ _vm->_system->delayMillis(100);
}
}
}
@@ -308,15 +311,15 @@ void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
if (_vm->_hotspots[i + startHotspot].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
if (_sliderState & (1 << (24 - i)))
- _vm->_gfx->changeCursor(kRivenOpenHandCursor);
+ _vm->_cursor->setCursor(kRivenOpenHandCursor);
else
- _vm->_gfx->changeCursor(kRivenMainCursor);
+ _vm->_cursor->setCursor(kRivenMainCursor);
break;
}
}
}
-void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot) {
+void RivenExternal::dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot) {
int16 foundSlider = -1;
for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
@@ -335,7 +338,7 @@ void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 reset
return;
// We've clicked down, so show the closed hand cursor
- _vm->_gfx->changeCursor(kRivenClosedHandCursor);
+ _vm->_cursor->setCursor(kRivenClosedHandCursor);
bool done = false;
while (!done) {
@@ -351,7 +354,7 @@ void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 reset
// Now play a click sound and redraw
_vm->_sound->playSound(soundId);
- drawDomeSliders(bitmapId, startHotspot);
+ drawDomeSliders(startHotspot);
} else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1].rect.contains(event.mouse)) {
// We've moved the slider left one space
_sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
@@ -360,7 +363,7 @@ void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 reset
// Now play a click sound and redraw
_vm->_sound->playSound(soundId);
- drawDomeSliders(bitmapId, startHotspot);
+ drawDomeSliders(startHotspot);
} else
_vm->_system->updateScreen(); // A normal update for the cursor
break;
@@ -378,7 +381,7 @@ void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 reset
checkDomeSliders(resetSlidersHotspot, openDomeHotspot);
}
-void RivenExternal::drawDomeSliders(uint16 bitmapId, uint16 startHotspot) {
+void RivenExternal::drawDomeSliders(uint16 startHotspot) {
Common::Rect dstAreaRect = Common::Rect(200, 250, 420, 319);
// On pspit, the rect is different by two pixels
@@ -386,6 +389,9 @@ void RivenExternal::drawDomeSliders(uint16 bitmapId, uint16 startHotspot) {
if (_vm->getCurStack() == pspit)
dstAreaRect.translate(-2, 0);
+ // Find out bitmap id
+ uint16 bitmapId = _vm->findResourceID(ID_TBMP, "*sliders*");
+
for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
Common::Rect srcRect = _vm->_hotspots[startHotspot + i].rect;
srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
@@ -652,6 +658,26 @@ void RivenExternal::xaenablemenuintro(uint16 argc, uint16 *argv) {
_vm->_gfx->showInventory();
}
+void RivenExternal::xademoquit(uint16 argc, uint16 *argv) {
+ // Exactly as it says on the tin. In the demo, this function quits.
+ _vm->setGameOver();
+}
+
+void RivenExternal::xaexittomain(uint16 argc, uint16 *argv) {
+ // One could potentially implement this function, but there would be no
+ // point. This function is only used in the demo's aspit card 9 update
+ // screen script. However, card 9 is not accessible from the game without
+ // jumping to the card and there's nothing going on in the card so it
+ // never gets called. There's also no card 9 in the full game, so the
+ // functionality of this card was likely removed before release. The
+ // demo executable references some other external commands relating to
+ // setting and getting the volume, as well as drawing the volume. I'd
+ // venture to guess that this would have been some sort of options card
+ // replaced with the Windows/Mac API in the final product.
+ //
+ // Yeah, this function is just dummied and holds a big comment ;)
+}
+
// ------------------------------------------------------------------------------------
// bspit (Bookmaking Island) external commands
// ------------------------------------------------------------------------------------
@@ -801,7 +827,7 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
else if (argv[0] == 2)
_vm->_sound->playSLST(1, _vm->getCurCard());
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_video->playMovieBlocking(11);
}
@@ -833,7 +859,7 @@ void RivenExternal::xbcheckcatch(uint16 argc, uint16 *argv) {
void RivenExternal::xbait(uint16 argc, uint16 *argv) {
// Set the cursor to the pellet
- _vm->_gfx->changeCursor(kRivenPelletCursor);
+ _vm->_cursor->setCursor(kRivenPelletCursor);
// Loop until the player lets go (or quits)
Common::Event event;
@@ -852,7 +878,7 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
}
// Set back the cursor
- _vm->_gfx->changeCursor(kRivenMainCursor);
+ _vm->_cursor->setCursor(kRivenMainCursor);
// Set the bait if we put it on the plate
if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
@@ -872,7 +898,7 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
// Remove the pellet from the plate and put it in your hand
_vm->_gfx->drawPLST(3);
_vm->_gfx->updateScreen();
- _vm->_gfx->changeCursor(kRivenPelletCursor);
+ _vm->_cursor->setCursor(kRivenPelletCursor);
// Loop until the player lets go (or quits)
Common::Event event;
@@ -891,7 +917,7 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
}
// Set back the cursor
- _vm->_gfx->changeCursor(kRivenMainCursor);
+ _vm->_cursor->setCursor(kRivenMainCursor);
// Set the bait if we put it on the plate, remove otherwise
if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
@@ -912,11 +938,11 @@ void RivenExternal::xbisland190_opencard(uint16 argc, uint16 *argv) {
}
void RivenExternal::xbisland190_resetsliders(uint16 argc, uint16 *argv) {
- resetDomeSliders(701, 41, 2);
+ resetDomeSliders(41, 2);
}
void RivenExternal::xbisland190_slidermd(uint16 argc, uint16 *argv) {
- dragDomeSlider(701, 41, 27, 28, 2);
+ dragDomeSlider(41, 27, 28, 2);
}
void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) {
@@ -942,7 +968,7 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
bool done = false;
// Set the cursor to the closed position
- _vm->_gfx->changeCursor(kRivenClosedHandCursor);
+ _vm->_cursor->setCursor(kRivenClosedHandCursor);
_vm->_system->updateScreen();
while (!done) {
@@ -959,24 +985,24 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
// FIXME: These values for changes in x/y could be tweaked.
if (*valve == 0 && changeY <= -10) {
*valve = 1;
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_video->playMovieBlocking(2);
_vm->refreshCard();
} else if (*valve == 1) {
if (changeX >= 0 && changeY >= 10) {
*valve = 0;
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_video->playMovieBlocking(3);
_vm->refreshCard();
} else if (changeX <= -10 && changeY <= 10) {
*valve = 2;
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_video->playMovieBlocking(1);
_vm->refreshCard();
}
} else if (*valve == 2 && changeX >= 10) {
*valve = 1;
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_video->playMovieBlocking(4);
_vm->refreshCard();
}
@@ -1031,15 +1057,15 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
}
void RivenExternal::xgisland25_opencard(uint16 argc, uint16 *argv) {
- checkDomeSliders(29, 30);
+ checkDomeSliders(28, 29);
}
void RivenExternal::xgisland25_resetsliders(uint16 argc, uint16 *argv) {
- resetDomeSliders(161, 16, 2);
+ resetDomeSliders(16, 2);
}
void RivenExternal::xgisland25_slidermd(uint16 argc, uint16 *argv) {
- dragDomeSlider(161, 16, 29, 30, 2);
+ dragDomeSlider(16, 28, 29, 2);
}
void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) {
@@ -1056,7 +1082,7 @@ void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) {
void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
// Play the deactivation of a pool if one is active and a different one is activated
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_video->playMovieBlocking(*_vm->getVar("glkbtns") * 2);
}
@@ -1264,11 +1290,11 @@ void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
// Run the gallows's carriage
- _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor
_vm->_video->playMovieBlocking(1); // Play handle movie
_vm->_gfx->scheduleTransition(15); // Set pan down transition
_vm->changeToCard(_vm->matchRMAPToCard(0x18e77)); // Change to card facing up
- _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor (again)
+ _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor (again)
_vm->_video->playMovieBlocking(4); // Play carriage beginning to drop
_vm->_gfx->scheduleTransition(14); // Set pan up transition
_vm->changeToCard(_vm->matchRMAPToCard(0x183a9)); // Change to card looking straight again
@@ -1302,16 +1328,16 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
_vm->_system->delayMillis(10);
}
- _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor
if (gotClick) {
_vm->_gfx->scheduleTransition(16); // Schedule dissolve transition
_vm->changeToCard(_vm->matchRMAPToCard(0x18d4d)); // Move forward
- _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor
_vm->_system->delayMillis(500); // Delay a half second before changing again
_vm->_gfx->scheduleTransition(12); // Schedule pan left transition
_vm->changeToCard(_vm->matchRMAPToCard(0x18ab5)); // Turn right
- _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor
_vm->_video->playMovieBlocking(1); // Play carriage ride movie
_vm->changeToCard(_vm->matchRMAPToCard(0x17167)); // We have arrived at the top
} else
@@ -1319,11 +1345,11 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
}
void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
- resetDomeSliders(_vm->getFeatures() & GF_DVD ? 547 : 548, 81, 2);
+ resetDomeSliders(81, 2);
}
void RivenExternal::xjdome25_slidermd(uint16 argc, uint16 *argv) {
- dragDomeSlider(_vm->getFeatures() & GF_DVD ? 547: 548, 81, 29, 28, 2);
+ dragDomeSlider(81, 29, 28, 2);
}
void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) {
@@ -1344,7 +1370,7 @@ int RivenExternal::jspitElevatorLoop() {
Common::Event event;
int changeLevel = 0;
- _vm->_gfx->changeCursor(kRivenClosedHandCursor);
+ _vm->_cursor->setCursor(kRivenClosedHandCursor);
_vm->_system->updateScreen();
for (;;) {
while (_vm->_system->getEventManager()->pollEvent(event)) {
@@ -1360,7 +1386,7 @@ int RivenExternal::jspitElevatorLoop() {
_vm->_system->updateScreen();
break;
case Common::EVENT_LBUTTONUP:
- _vm->_gfx->changeCursor(kRivenMainCursor);
+ _vm->_cursor->setCursor(kRivenMainCursor);
_vm->_system->updateScreen();
return changeLevel;
default:
@@ -1502,7 +1528,7 @@ void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) {
void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
// Hide the cursor
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
// Let's hook onto our video
VideoHandle video = _vm->_video->findVideoHandle(argv[0]);
@@ -1542,9 +1568,9 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
// Update our hotspot stuff
if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
- _vm->_gfx->changeCursor(kRivenOpenHandCursor);
+ _vm->_cursor->setCursor(kRivenOpenHandCursor);
else
- _vm->_gfx->changeCursor(kRivenMainCursor);
+ _vm->_cursor->setCursor(kRivenMainCursor);
// OK, Gehn has opened the trap book and has asked us to go in. Let's watch
// and see what the player will do...
@@ -1556,9 +1582,9 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
- _vm->_gfx->changeCursor(kRivenOpenHandCursor);
+ _vm->_cursor->setCursor(kRivenOpenHandCursor);
else
- _vm->_gfx->changeCursor(kRivenMainCursor);
+ _vm->_cursor->setCursor(kRivenMainCursor);
updateScreen = false; // Don't update twice, changing the cursor already updates the screen
break;
case Common::EVENT_LBUTTONUP:
@@ -1566,7 +1592,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
// OK, we've used the trap book! We go for ride lady!
_vm->_scriptMan->stopAllScripts(); // Stop all running scripts (so we don't remain in the cage)
_vm->_video->stopVideos(); // Stop all videos
- _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_cursor->setCursor(kRivenHideCursor); // Hide the cursor
_vm->_gfx->drawPLST(3); // Black out the screen
_vm->_gfx->updateScreen(); // Update the screen
_vm->_sound->playSound(0); // Play the link sound
@@ -1595,7 +1621,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
return;
// Hide the cursor again
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
// If there was no click and this is the third time Gehn asks us to
// use the trap book, he will shoot the player. Dead on arrival.
@@ -1692,7 +1718,7 @@ uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
// Hide the cursor
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
uint32 *prisonCombo = _vm->getVar("pcorrectorder");
uint32 soundTime = _vm->_system->getMillis() - 500; // Start the first sound instantly
@@ -1762,11 +1788,11 @@ void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) {
}
void RivenExternal::xpisland25_resetsliders(uint16 argc, uint16 *argv) {
- resetDomeSliders(58, 10, 6);
+ resetDomeSliders(10, 6);
}
void RivenExternal::xpisland25_slidermd(uint16 argc, uint16 *argv) {
- dragDomeSlider(58, 10, 31, 5, 6);
+ dragDomeSlider(10, 31, 5, 6);
}
void RivenExternal::xpisland25_slidermw(uint16 argc, uint16 *argv) {
@@ -1853,7 +1879,7 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
} else {
// ...the telescope can't move down anymore.
// Play the sound of not being able to move
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_sound->playSoundBlocking(13);
}
} else {
@@ -1880,7 +1906,7 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
// Check if we can't move up anymore
if (*telescopePos == 5) {
// Play the sound of not being able to move
- _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_cursor->setCursor(kRivenHideCursor);
_vm->_sound->playSoundBlocking(13);
return;
}
@@ -1988,7 +2014,7 @@ void RivenExternal::xt7500_checkmarbles(uint16 argc, uint16 *argv) {
void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) {
// Draw the small marbles when we're a step away from the waffle
- uint16 baseBitmapId = (_vm->getFeatures() & GF_DVD) ? 539 : 526;
+ uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred");
bool waffleDown = *_vm->getVar("twaffle") != 0;
// Note that each of the small marble images is exactly 4x2
@@ -2146,11 +2172,11 @@ void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) {
}
void RivenExternal::xtisland5056_resetsliders(uint16 argc, uint16 *argv) {
- resetDomeSliders(_vm->getFeatures() & GF_DVD ? 813 : 798, 37, 3);
+ resetDomeSliders(37, 3);
}
void RivenExternal::xtisland5056_slidermd(uint16 argc, uint16 *argv) {
- dragDomeSlider(_vm->getFeatures() & GF_DVD ? 813 : 798, 37, 29, 30, 3);
+ dragDomeSlider(37, 29, 30, 3);
}
void RivenExternal::xtisland5056_slidermw(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index 1f012c82d9..49cafb6b4a 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -65,11 +65,11 @@ private:
void runCredits(uint16 video);
void runDomeCheck();
void runDomeButtonMovie();
- void resetDomeSliders(uint16 bitmapId, uint16 soundId, uint16 startHotspot);
+ void resetDomeSliders(uint16 soundId, uint16 startHotspot);
void checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDomeHotspot);
void checkSliderCursorChange(uint16 startHotspot);
- void dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot);
- void drawDomeSliders(uint16 bitmapId, uint16 startHotspot);
+ void dragDomeSlider(uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot);
+ void drawDomeSliders(uint16 startHotspot);
void drawMarbles();
void setMarbleHotspots();
@@ -100,6 +100,8 @@ private:
void xalaunchbrowser(uint16 argc, uint16 *argv);
void xadisablemenuintro(uint16 argc, uint16 *argv);
void xaenablemenuintro(uint16 argc, uint16 *argv);
+ void xademoquit(uint16 argc, uint16 *argv);
+ void xaexittomain(uint16 argc, uint16 *argv);
// -----------------------------------------------------
// bspit (Boiler Island) external commands
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index c63a3f98fb..881e171b84 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -102,10 +102,15 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
debug(0, "Loading game from \'%s\'", filename.c_str());
MohawkArchive *mhk = new MohawkArchive();
- mhk->open(loadFile);
+
+ if (!mhk->open(loadFile)) {
+ warning("Save file is not a Mohawk archive");
+ delete mhk;
+ return false;
+ }
// First, let's make sure we're using a saved game file from this version of Riven by checking the VERS resource
- Common::SeekableReadStream *vers = mhk->getRawData(ID_VERS, 1);
+ Common::SeekableReadStream *vers = mhk->getResource(ID_VERS, 1);
uint32 saveGameVersion = vers->readUint32BE();
delete vers;
if ((saveGameVersion == kCDSaveGameVersion && (_vm->getFeatures() & GF_DVD))
@@ -116,7 +121,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
}
// Now, we'll read in the variable values.
- Common::SeekableReadStream *vars = mhk->getRawData(ID_VARS, 1);
+ Common::SeekableReadStream *vars = mhk->getResource(ID_VARS, 1);
Common::Array<uint32> rawVariables;
while (!vars->eos()) {
@@ -129,7 +134,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
// Next, we set the variables based on the name found by the index in the VARS resource.
// TODO: Merge with code in mohawk.cpp for loading names?
- Common::SeekableReadStream *names = mhk->getRawData(ID_NAME, 1);
+ Common::SeekableReadStream *names = mhk->getResource(ID_NAME, 1);
uint16 namesCount = names->readUint16BE();
uint16 *stringOffsets = new uint16[namesCount];
@@ -183,7 +188,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
_vm->_zipModeData.clear();
// Finally, we load in zip mode data.
- Common::SeekableReadStream *zips = mhk->getRawData(ID_ZIPS, 1);
+ Common::SeekableReadStream *zips = mhk->getResource(ID_ZIPS, 1);
uint16 zipsRecordCount = zips->readUint16BE();
for (uint16 i = 0; i < zipsRecordCount; i++) {
ZipMode zip;
diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h
index 9cc9bc3737..fbd9f7cb36 100644
--- a/engines/mohawk/riven_saveload.h
+++ b/engines/mohawk/riven_saveload.h
@@ -28,6 +28,7 @@
#include "common/savefile.h"
#include "common/str.h"
+#include "common/memstream.h"
namespace Mohawk {
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 30d1d727eb..8d0743340c 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "mohawk/cursors.h"
#include "mohawk/graphics.h"
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
@@ -157,18 +158,18 @@ void RivenScript::setupOpcodes() {
static void printTabs(byte tabs) {
for (byte i = 0; i < tabs; i++)
- printf ("\t");
+ debugN("\t");
}
-void RivenScript::dumpScript(Common::StringArray varNames, Common::StringArray xNames, byte tabs) {
+void RivenScript::dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
if (_stream->pos() != 0)
_stream->seek(0);
- printTabs(tabs); printf ("Stream Type %d:\n", _scriptType);
+ printTabs(tabs); debugN("Stream Type %d:\n", _scriptType);
dumpCommands(varNames, xNames, tabs + 1);
}
-void RivenScript::dumpCommands(Common::StringArray varNames, Common::StringArray xNames, byte tabs) {
+void RivenScript::dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs) {
uint16 commandCount = _stream->readUint16BE();
for (uint16 i = 0; i < commandCount; i++) {
@@ -178,50 +179,50 @@ void RivenScript::dumpCommands(Common::StringArray varNames, Common::StringArray
if (_stream->readUint16BE() != 2)
warning ("if-then-else unknown value is not 2");
uint16 var = _stream->readUint16BE();
- printTabs(tabs); printf("switch (%s) {\n", varNames[var].c_str());
+ printTabs(tabs); debugN("switch (%s) {\n", varNames[var].c_str());
uint16 logicBlockCount = _stream->readUint16BE();
for (uint16 j = 0; j < logicBlockCount; j++) {
uint16 varCheck = _stream->readUint16BE();
printTabs(tabs + 1);
if (varCheck == 0xFFFF)
- printf("default:\n");
+ debugN("default:\n");
else
- printf("case %d:\n", varCheck);
+ debugN("case %d:\n", varCheck);
dumpCommands(varNames, xNames, tabs + 2);
- printTabs(tabs + 2); printf("break;\n");
+ printTabs(tabs + 2); debugN("break;\n");
}
- printTabs(tabs); printf("}\n");
+ printTabs(tabs); debugN("}\n");
} else if (command == 7) { // Use the variable name
_stream->readUint16BE(); // Skip the opcode var count
printTabs(tabs);
uint16 var = _stream->readUint16BE();
- printf("%s = %d;\n", varNames[var].c_str(), _stream->readUint16BE());
+ debugN("%s = %d;\n", varNames[var].c_str(), _stream->readUint16BE());
} else if (command == 17) { // Use the external command name
_stream->readUint16BE(); // Skip the opcode var count
printTabs(tabs);
- printf("%s(", xNames[_stream->readUint16BE()].c_str());
+ debugN("%s(", xNames[_stream->readUint16BE()].c_str());
uint16 varCount = _stream->readUint16BE();
for (uint16 j = 0; j < varCount; j++) {
- printf("%d", _stream->readUint16BE());
+ debugN("%d", _stream->readUint16BE());
if (j != varCount - 1)
- printf(", ");
+ debugN(", ");
}
- printf (");\n");
+ debugN(");\n");
} else if (command == 24) { // Use the variable name
_stream->readUint16BE(); // Skip the opcode var count
printTabs(tabs);
uint16 var = _stream->readUint16BE();
- printf ("%s += %d;\n", varNames[var].c_str(), _stream->readUint16BE());
+ debugN("%s += %d;\n", varNames[var].c_str(), _stream->readUint16BE());
} else {
printTabs(tabs);
uint16 varCount = _stream->readUint16BE();
- printf("%s(", _opcodes[command].desc);
+ debugN("%s(", _opcodes[command].desc);
for (uint16 j = 0; j < varCount; j++) {
- printf("%d", _stream->readUint16BE());
+ debugN("%d", _stream->readUint16BE());
if (j != varCount - 1)
- printf(", ");
+ debugN(", ");
}
- printf(");\n");
+ debugN(");\n");
}
}
}
@@ -344,6 +345,11 @@ void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) {
// Play the requested sound list
_vm->_sound->playSLST(slstRecord);
_vm->_activatedSLST = true;
+
+ delete[] slstRecord.sound_ids;
+ delete[] slstRecord.volumes;
+ delete[] slstRecord.balances;
+ delete[] slstRecord.u2;
}
// Command 4: play local tWAV resource (twav_id, volume, block)
@@ -396,7 +402,7 @@ void RivenScript::clearSLST(uint16 op, uint16 argc, uint16 *argv) {
// Command 13: set mouse cursor (cursor_id)
void RivenScript::changeCursor(uint16 op, uint16 argc, uint16 *argv) {
debug(2, "Change to cursor %d", argv[0]);
- _vm->_gfx->changeCursor(argv[0]);
+ _vm->_cursor->setCursor(argv[0]);
}
// Command 14: pause script execution (delay in ms, u1)
@@ -513,14 +519,14 @@ void RivenScript::fadeAmbientSounds(uint16 op, uint16 argc, uint16 *argv) {
// Command 38: Play a movie with extra parameters (movie id, delay high, delay low, record type, record id)
void RivenScript::complexPlayMovie(uint16 op, uint16 argc, uint16 *argv) {
warning("STUB: complexPlayMovie");
- printf ("\tMovie ID = %d\n", argv[0]);
- printf ("\tDelay = %d\n", (argv[1] << 16) + argv[2]);
+ debugN("\tMovie ID = %d\n", argv[0]);
+ debugN("\tDelay = %d\n", (argv[1] << 16) + argv[2]);
if (argv[3] == 0) {
- printf ("\tDraw PLST %d\n", argv[4]);
+ debugN("\tDraw PLST %d\n", argv[4]);
} else if (argv[3] == 40) {
- printf ("\tPlay SLST %d\n", argv[4]);
+ debugN("\tPlay SLST %d\n", argv[4]);
} else {
- error ("Unknown complexPlayMovie record type %d", argv[3]);
+ error("Unknown complexPlayMovie record type %d", argv[3]);
}
}
@@ -552,7 +558,7 @@ void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) {
// Command 43: activate BLST record (card hotspot enabling lists)
void RivenScript::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
- Common::SeekableReadStream* blst = _vm->getRawData(ID_BLST, _vm->getCurCard());
+ Common::SeekableReadStream* blst = _vm->getResource(ID_BLST, _vm->getCurCard());
uint16 recordCount = blst->readUint16BE();
for (uint16 i = 0; i < recordCount; i++) {
@@ -571,7 +577,7 @@ void RivenScript::activateBLST(uint16 op, uint16 argc, uint16 *argv) {
// Command 44: activate FLST record (information on which SFXE resource this card should use)
void RivenScript::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
- Common::SeekableReadStream* flst = _vm->getRawData(ID_FLST, _vm->getCurCard());
+ Common::SeekableReadStream* flst = _vm->getResource(ID_FLST, _vm->getCurCard());
uint16 recordCount = flst->readUint16BE();
for (uint16 i = 0; i < recordCount; i++) {
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index a85cde1702..57ffa67ca0 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -57,7 +57,7 @@ public:
~RivenScript();
void runScript();
- void dumpScript(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
+ void dumpScript(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
uint16 getScriptType() { return _scriptType; }
uint16 getParentStack() { return _parentStack; }
uint16 getParentCard() { return _parentCard; }
@@ -80,7 +80,7 @@ private:
uint16 _scriptType, _parentStack, _parentCard;
bool _isRunning, _continueRunning;
- void dumpCommands(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
+ void dumpCommands(const Common::StringArray &varNames, const Common::StringArray &xNames, byte tabs);
void processCommands(bool runCommands);
static uint32 calculateCommandSize(Common::SeekableReadStream *script);
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index 4a8c923c01..d801907bd0 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -38,12 +38,6 @@ namespace Mohawk {
Sound::Sound(MohawkEngine* vm) : _vm(vm) {
_midiDriver = NULL;
_midiParser = NULL;
-
- for (uint32 i = 0; i < _handles.size(); i++) {
- _handles[i].handle = Audio::SoundHandle();
- _handles[i].type = kFreeHandle;
- }
-
initMidi();
}
@@ -81,6 +75,7 @@ Audio::SoundHandle *Sound::playSound(uint16 id, byte volume, bool loop) {
SndHandle *handle = getHandle();
handle->type = kUsedHandle;
+ handle->id = id;
Audio::AudioStream *audStream = NULL;
@@ -93,23 +88,23 @@ Audio::SoundHandle *Sound::playSound(uint16 id, byte volume, bool loop) {
// resource we're looking for. This saves a lot of space from
// repeated data.
if (_vm->hasResource(ID_MJMP, id)) {
- Common::SeekableReadStream *mjmpStream = _vm->getRawData(ID_MJMP, id);
+ Common::SeekableReadStream *mjmpStream = _vm->getResource(ID_MJMP, id);
id = mjmpStream->readUint16LE();
delete mjmpStream;
}
- audStream = Audio::makeWAVStream(_vm->getRawData(ID_MSND, id), DisposeAfterUse::YES);
+ audStream = Audio::makeWAVStream(_vm->getResource(ID_MSND, id), DisposeAfterUse::YES);
} else
- audStream = makeMohawkWaveStream(_vm->getRawData(ID_MSND, id));
+ audStream = makeMohawkWaveStream(_vm->getResource(ID_MSND, id));
break;
case GType_ZOOMBINI:
- audStream = makeMohawkWaveStream(_vm->getRawData(ID_SND, id));
+ audStream = makeMohawkWaveStream(_vm->getResource(ID_SND, id));
break;
case GType_LIVINGBOOKSV1:
- audStream = makeOldMohawkWaveStream(_vm->getRawData(ID_WAV, id));
+ audStream = makeOldMohawkWaveStream(_vm->getResource(ID_WAV, id));
break;
default:
- audStream = makeMohawkWaveStream(_vm->getRawData(ID_TWAV, id));
+ audStream = makeMohawkWaveStream(_vm->getResource(ID_TWAV, id));
}
if (audStream) {
@@ -141,7 +136,7 @@ void Sound::playMidi(uint16 id) {
assert(_midiDriver && _midiParser);
_midiParser->unloadMusic();
- Common::SeekableReadStream *midi = _vm->getRawData(ID_TMID, id);
+ Common::SeekableReadStream *midi = _vm->getResource(ID_TMID, id);
idTag = midi->readUint32BE();
assert(idTag == ID_MHWK);
@@ -177,7 +172,7 @@ byte Sound::convertRivenVolume(uint16 volume) {
}
void Sound::playSLST(uint16 index, uint16 card) {
- Common::SeekableReadStream *slstStream = _vm->getRawData(ID_SLST, card);
+ Common::SeekableReadStream *slstStream = _vm->getResource(ID_SLST, card);
SLSTRecord slstRecord;
uint16 recordCount = slstStream->readUint16BE();
@@ -292,7 +287,7 @@ void Sound::playSLSTSound(uint16 id, bool fade, bool loop, uint16 volume, int16
sndHandle.id = id;
_currentSLSTSounds.push_back(sndHandle);
- Audio::AudioStream *audStream = makeMohawkWaveStream(_vm->getRawData(ID_TWAV, id));
+ Audio::AudioStream *audStream = makeMohawkWaveStream(_vm->getResource(ID_TWAV, id));
// Loop here if necessary
if (loop)
@@ -462,11 +457,12 @@ Audio::AudioStream *Sound::makeOldMohawkWaveStream(Common::SeekableReadStream *s
if (header == 'Wv') { // Big Endian
rate = stream->readUint16BE();
- stream->skip(10); // Loop chunk, like the newer format?
+ stream->skip(10); // Unknown
size = stream->readUint32BE();
} else if (header == 'vW') { // Little Endian
+ stream->readUint16LE(); // Unknown
rate = stream->readUint16LE();
- stream->skip(10); // Loop chunk, like the newer format?
+ stream->skip(8); // Unknown
size = stream->readUint32LE();
} else
error("Could not find Old Mohawk Sound header");
@@ -484,6 +480,7 @@ SndHandle *Sound::getHandle() {
if (!_vm->_mixer->isSoundHandleActive(_handles[i].handle)) {
_handles[i].type = kFreeHandle;
+ _handles[i].id = 0;
return &_handles[i];
}
}
@@ -492,6 +489,7 @@ SndHandle *Sound::getHandle() {
SndHandle handle;
handle.handle = Audio::SoundHandle();
handle.type = kFreeHandle;
+ handle.id = 0;
_handles.push_back(handle);
return &_handles[_handles.size() - 1];
@@ -502,6 +500,16 @@ void Sound::stopSound() {
if (_handles[i].type == kUsedHandle) {
_vm->_mixer->stopHandle(_handles[i].handle);
_handles[i].type = kFreeHandle;
+ _handles[i].id = 0;
+ }
+}
+
+void Sound::stopSound(uint16 id) {
+ for (uint32 i = 0; i < _handles.size(); i++)
+ if (_handles[i].type == kUsedHandle && _handles[i].id == id) {
+ _vm->_mixer->stopHandle(_handles[i].handle);
+ _handles[i].type = kFreeHandle;
+ _handles[i].id = 0;
}
}
@@ -517,4 +525,12 @@ void Sound::resumeSound() {
_vm->_mixer->pauseHandle(_handles[i].handle, false);
}
+bool Sound::isPlaying(uint16 id) {
+ for (uint32 i = 0; i < _handles.size(); i++)
+ if (_handles[i].type == kUsedHandle && _handles[i].id == id)
+ return _vm->_mixer->isSoundHandleActive(_handles[i].handle);
+
+ return false;
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/sound.h b/engines/mohawk/sound.h
index f493130d35..e0674500fa 100644
--- a/engines/mohawk/sound.h
+++ b/engines/mohawk/sound.h
@@ -65,6 +65,7 @@ enum SndHandleType {
struct SndHandle {
Audio::SoundHandle handle;
SndHandleType type;
+ uint16 id;
};
struct SLSTSndHandle {
@@ -122,8 +123,10 @@ public:
void playSoundBlocking(uint16 id, byte volume = Audio::Mixer::kMaxChannelVolume);
void playMidi(uint16 id);
void stopSound();
+ void stopSound(uint16 id);
void pauseSound();
void resumeSound();
+ bool isPlaying(uint16 id);
// Riven-specific
void playSLST(uint16 index, uint16 card);
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index b7ee4c8a2c..ce2e2ae0f8 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -56,7 +56,7 @@ void VideoManager::stopVideos() {
_videoStreams.clear();
}
-void VideoManager::playMovie(Common::String filename, uint16 x, uint16 y, bool clearScreen) {
+void VideoManager::playMovie(const Common::String &filename, uint16 x, uint16 y, bool clearScreen) {
VideoHandle videoHandle = createVideoHandle(filename, x, y, false);
if (videoHandle == NULL_VID_HANDLE)
return;
@@ -70,7 +70,7 @@ void VideoManager::playMovie(Common::String filename, uint16 x, uint16 y, bool c
waitUntilMovieEnds(videoHandle);
}
-void VideoManager::playMovieCentered(Common::String filename, bool clearScreen) {
+void VideoManager::playMovieCentered(const Common::String &filename, bool clearScreen) {
VideoHandle videoHandle = createVideoHandle(filename, 0, 0, false);
if (videoHandle == NULL_VID_HANDLE)
return;
@@ -127,7 +127,7 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
_videoStreams[videoHandle].filename.clear();
}
-void VideoManager::playBackgroundMovie(Common::String filename, int16 x, int16 y, bool loop) {
+void VideoManager::playBackgroundMovie(const Common::String &filename, int16 x, int16 y, bool loop) {
VideoHandle videoHandle = createVideoHandle(filename, x, y, loop);
if (videoHandle == NULL_VID_HANDLE)
return;
@@ -219,7 +219,7 @@ bool VideoManager::updateBackgroundMovies() {
}
void VideoManager::activateMLST(uint16 mlstId, uint16 card) {
- Common::SeekableReadStream *mlstStream = _vm->getRawData(ID_MLST, card);
+ Common::SeekableReadStream *mlstStream = _vm->getResource(ID_MLST, card);
uint16 recordCount = mlstStream->readUint16BE();
for (uint16 i = 0; i < recordCount; i++) {
@@ -342,7 +342,7 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool
entry.loop = loop;
entry.enabled = true;
entry->setChunkBeginOffset(_vm->getResourceOffset(ID_TMOV, id));
- entry->load(_vm->getRawData(ID_TMOV, id));
+ entry->load(_vm->getResource(ID_TMOV, id));
// Search for any deleted videos so we can take a formerly used slot
for (uint32 i = 0; i < _videoStreams.size(); i++)
@@ -356,7 +356,7 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool
return _videoStreams.size() - 1;
}
-VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop) {
+VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop) {
// First, check to see if that video is already playing
for (uint32 i = 0; i < _videoStreams.size(); i++)
if (_videoStreams[i].filename == filename)
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 4c6ed05cef..c8aff5c4f9 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -72,9 +72,9 @@ public:
~VideoManager();
// Generic movie functions
- void playMovie(Common::String filename, uint16 x = 0, uint16 y = 0, bool clearScreen = false);
- void playMovieCentered(Common::String filename, bool clearScreen = true);
- void playBackgroundMovie(Common::String filename, int16 x = -1, int16 y = -1, bool loop = false);
+ void playMovie(const Common::String &filename, uint16 x = 0, uint16 y = 0, bool clearScreen = false);
+ void playMovieCentered(const Common::String &filename, bool clearScreen = true);
+ void playBackgroundMovie(const Common::String &filename, int16 x = -1, int16 y = -1, bool loop = false);
bool updateBackgroundMovies();
void pauseVideos();
void resumeVideos();
@@ -107,7 +107,7 @@ private:
Common::Array<VideoEntry> _videoStreams;
VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop);
- VideoHandle createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop);
+ VideoHandle createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop);
void waitUntilMovieEnds(VideoHandle videoHandle);
};
diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp
index e00a087923..f67a77aa21 100644
--- a/engines/parallaction/detection.cpp
+++ b/engines/parallaction/detection.cpp
@@ -325,7 +325,7 @@ int ParallactionMetaEngine::getMaximumSaveSlot() const { return 99; }
void ParallactionMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String filename = ConfMan.getDomain(target)->getVal("gameid");
- filename += Common::String::printf(".0%02d", slot);
+ filename += Common::String::format(".0%02d", slot);
g_system->getSavefileManager()->removeSavefile(filename);
}
diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp
index 0b893fbe2c..12455af5e7 100644
--- a/engines/parallaction/disk_ns.cpp
+++ b/engines/parallaction/disk_ns.cpp
@@ -25,6 +25,8 @@
#include "common/config-manager.h"
#include "common/fs.h"
+#include "common/memstream.h"
+#include "common/substream.h"
#include "parallaction/parser.h"
#include "parallaction/parallaction.h"
@@ -1087,4 +1089,4 @@ Common::SeekableReadStream* AmigaDisk_ns::loadSound(const char* name) {
return tryOpenFile(path);
}
-} // namespace Parallaction
+} // End of namespace Parallaction
diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp
index d1c67f1338..18f469f0a2 100644
--- a/engines/parallaction/font.cpp
+++ b/engines/parallaction/font.cpp
@@ -24,7 +24,7 @@
*/
#include "common/endian.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "parallaction/parallaction.h"
@@ -574,7 +574,7 @@ void AmigaFont::blitData(byte c) {
}
uint16 AmigaFont::width(byte c) {
-// printf("kern(%i) = %i, space(%i) = %i\t", c, getKerning(c), c, getSpacing(c));
+// debug("kern(%i) = %i, space(%i) = %i\t", c, getKerning(c), c, getSpacing(c));
return getKerning(c) + getSpacing(c);
}
@@ -642,7 +642,7 @@ Font *AmigaDisk_ns::createFont(const char *name, Common::SeekableReadStream &str
}
Font *DosDisk_br::createFont(const char *name, Common::ReadStream &stream) {
-// printf("DosDisk_br::createFont(%s)\n", name);
+// debug("DosDisk_br::createFont(%s)", name);
Font *font;
if (_vm->getFeatures() & GF_DEMO) {
@@ -670,7 +670,6 @@ GfxObj* DosDisk_br::createInventoryObjects(Common::SeekableReadStream &stream) {
void Parallaction_ns::initFonts() {
-
if (getPlatform() == Common::kPlatformPC) {
_dialogueFont = _disk->loadFont("comic");
_labelFont = _disk->loadFont("topaz");
@@ -683,7 +682,6 @@ void Parallaction_ns::initFonts() {
_menuFont = _disk->loadFont("slide");
_introFont = _disk->loadFont("intro");
}
-
}
void Parallaction_br::initFonts() {
@@ -701,4 +699,4 @@ void Parallaction_br::initFonts() {
}
}
-}
+} // End of namespace Parallaction
diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp
index a1926fc197..6c39b2e696 100644
--- a/engines/parallaction/gfxbase.cpp
+++ b/engines/parallaction/gfxbase.cpp
@@ -322,7 +322,7 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
uint line = 0, col = 0;
uint xAccum = 0, yAccum = 0;
- uint inc = width * (100 - scale);
+ uint inc = width * (100 - scale);
uint thr = width * 100;
for (uint16 i = 0; i < srcRect.height(); i++) {
diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp
index 9f50236360..51d3ba5799 100644
--- a/engines/parallaction/gui_ns.cpp
+++ b/engines/parallaction/gui_ns.cpp
@@ -118,6 +118,7 @@ public:
ChooseLanguageInputState_NS(Parallaction *vm, MenuInputHelper *helper) : MenuInputState("chooselanguage", helper), _vm(vm) {
_allowChoice = false;
_nextState = "selectgame";
+ _label = 0;
_dosLanguageSelectBlocks[0] = Common::Rect( 80, 110, 128, 180 ); // Italian
_dosLanguageSelectBlocks[1] = Common::Rect( 129, 85, 177, 155 ); // French
@@ -147,7 +148,6 @@ public:
_blocks = _dosLanguageSelectBlocks;
}
- _label = 0;
_language = -1;
_allowChoice = true;
}
diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp
index 2de7fe9d64..1c0ab9defd 100644
--- a/engines/parallaction/parallaction.cpp
+++ b/engines/parallaction/parallaction.cpp
@@ -1004,4 +1004,4 @@ void Parallaction::scheduleLocationSwitch(const char *location) {
}
-} // namespace Parallaction
+} // End of namespace Parallaction
diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h
index 7bbdf79f1c..a8a57ed2d8 100644
--- a/engines/parallaction/parallaction.h
+++ b/engines/parallaction/parallaction.h
@@ -48,8 +48,9 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Nippon Safes Inc. (complete)
+ * - The Big Red Adventure (work in progress)
*/
namespace Parallaction {
@@ -605,7 +606,7 @@ private:
extern Parallaction *_vm;
-} // namespace Parallaction
+} // End of namespace Parallaction
#endif
diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp
index 470c698a21..1cbd857ac9 100644
--- a/engines/parallaction/parallaction_br.cpp
+++ b/engines/parallaction/parallaction_br.cpp
@@ -154,7 +154,8 @@ Common::Error Parallaction_br::go() {
while (!shouldQuit()) {
if (getFeatures() & GF_DEMO) {
- scheduleLocationSwitch("camalb.1");
+ scheduleLocationSwitch("camalb");
+ _nextPart = 1;
_input->_inputMode = Input::kInputModeGame;
} else {
startGui(splash);
diff --git a/engines/parallaction/parser.cpp b/engines/parallaction/parser.cpp
index df1e91e8b4..c964b74512 100644
--- a/engines/parallaction/parser.cpp
+++ b/engines/parallaction/parser.cpp
@@ -257,5 +257,4 @@ void Parser::parseStatement() {
(*(*_currentOpcodes)[_lookup])();
}
-
-} // namespace Parallaction
+} // End of namespace Parallaction
diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h
index 5eb26e9fa1..7887b2192f 100644
--- a/engines/parallaction/parser.h
+++ b/engines/parallaction/parser.h
@@ -409,13 +409,6 @@ public:
virtual void parse(Script *script, ProgramPtr program);
};
-
-} // namespace Parallaction
+} // End of namespace Parallaction
#endif
-
-
-
-
-
-
diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp
index ff24a06ceb..994cfa46fb 100644
--- a/engines/parallaction/parser_ns.cpp
+++ b/engines/parallaction/parser_ns.cpp
@@ -23,7 +23,6 @@
*
*/
-
#include "parallaction/parallaction.h"
#include "parallaction/parser.h"
#include "parallaction/sound.h"
@@ -1284,7 +1283,7 @@ DECLARE_ZONE_PARSER(commands) {
DECLARE_ZONE_PARSER(label) {
debugC(7, kDebugParser, "ZONE_PARSER(label) ");
-// printf("label: %s", _tokens[1]);
+// debug("label: %s", _tokens[1]);
ctxt.z->_label = _vm->_gfx->renderFloatingLabel(_vm->_labelFont, _tokens[1]);
ctxt.z->_flags &= ~kFlagsNoName;
}
diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp
index 66515127da..855dc12105 100644
--- a/engines/parallaction/saveload.cpp
+++ b/engines/parallaction/saveload.cpp
@@ -29,7 +29,7 @@
#include "gui/dialog.h"
#include "gui/saveload.h"
#include "gui/widget.h"
-#include "gui/ListWidget.h"
+#include "gui/widgets/list.h"
#include "gui/message.h"
#include "parallaction/parallaction.h"
diff --git a/engines/parallaction/staticres.cpp b/engines/parallaction/staticres.cpp
index fa1ac910e7..84db3af9a4 100644
--- a/engines/parallaction/staticres.cpp
+++ b/engines/parallaction/staticres.cpp
@@ -54,216 +54,215 @@ byte Input::_resMouseArrow_NS[256] = {
TODO: The cursor data should be adjusted by adding 0x10 to each byte, because the bitmap
must be drawn using the background palette.
*/
-byte Input::_resMouseArrow_BR_Amiga[512] =
-{
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
- 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+byte Input::_resMouseArrow_BR_Amiga[512] = {
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/*
This palette snippet is used for animations in Big Red Adventure.
*/
byte _braAmigaFramesDefaultPalette[48] = {
- 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00,
- 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE,
- 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00,
+ 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE,
+ 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF,
};
byte _amigaTopazFont[2600] = {
- 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79,
- 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
- 0x00, 0x1a, 0x0f, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x45, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x74, 0x00, 0x08,
- 0x00, 0x40, 0x00, 0x08, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x20, 0xff, 0x00, 0x00, 0x00, 0x6e,
- 0x00, 0xbe, 0x00, 0x00, 0x06, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
- 0x6c, 0x6c, 0x18, 0x00, 0x38, 0x18, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, 0x18,
- 0x3c, 0x3c, 0x1c, 0x7e, 0x1c, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x3c,
- 0x7c, 0x1e, 0x78, 0x7e, 0x7e, 0x3c, 0x66, 0x3c, 0x06, 0xc6, 0x60, 0xc6, 0xc6, 0x3c, 0x7c, 0x78,
- 0x7c, 0x3c, 0x7e, 0x66, 0x66, 0xc6, 0xc3, 0xc3, 0xfe, 0x3c, 0xc0, 0x3c, 0x10, 0x00, 0x18, 0x00,
- 0x60, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x60, 0x18, 0x0c, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x70, 0x72, 0x0f, 0x00, 0x18,
- 0x00, 0x1c, 0x42, 0xc3, 0x18, 0x3c, 0x66, 0x7e, 0x1c, 0x00, 0x3e, 0x7e, 0x7e, 0x3c, 0x18, 0x78,
- 0x78, 0x18, 0x00, 0x3e, 0x00, 0x00, 0x30, 0x38, 0x00, 0x40, 0x40, 0xc0, 0x18, 0x3c, 0x3c, 0x7e,
- 0x06, 0x66, 0x18, 0x7e, 0x7e, 0x36, 0x0c, 0x0c, 0x18, 0x3c, 0xc6, 0x3c, 0x60, 0x76, 0x18, 0x00,
- 0x0c, 0x7e, 0x71, 0x66, 0x00, 0x66, 0x60, 0x0e, 0x7e, 0x66, 0x18, 0x6e, 0x3c, 0x00, 0x18, 0x7e,
- 0x06, 0x66, 0x18, 0x00, 0x7e, 0x34, 0x0c, 0x0c, 0x18, 0x0c, 0x60, 0x00, 0x18, 0x3c, 0x0c, 0x00,
- 0x0c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x7e, 0x00, 0x18, 0x3c, 0x00, 0x18, 0x6c, 0x6c,
- 0x3e, 0x66, 0x6c, 0x18, 0x18, 0x18, 0x66, 0x18, 0x00, 0x00, 0x00, 0x06, 0x66, 0x38, 0x66, 0x66,
- 0x3c, 0x60, 0x30, 0x06, 0x66, 0x66, 0x18, 0x18, 0x06, 0x00, 0x60, 0x66, 0xc6, 0x66, 0x66, 0x30,
- 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x06, 0xcc, 0x60, 0xee, 0xe6, 0x66, 0x66, 0xcc, 0x66, 0x66,
- 0x18, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x0c, 0x30, 0x60, 0x0c, 0x38, 0x00, 0x18, 0x00, 0x60, 0x00,
- 0x06, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x9c, 0x3c, 0x7e, 0x00, 0x0c, 0x36,
- 0x3c, 0x66, 0x18, 0x60, 0x66, 0x81, 0x24, 0x33, 0x06, 0x81, 0x00, 0x66, 0x18, 0x0c, 0x0c, 0x30,
- 0x00, 0x7a, 0x00, 0x00, 0x70, 0x44, 0xcc, 0xc6, 0xc6, 0x23, 0x00, 0x66, 0x18, 0x00, 0x1c, 0x00,
- 0x24, 0x60, 0x00, 0x1c, 0x18, 0x18, 0x00, 0x66, 0xcc, 0x00, 0x60, 0x3c, 0x30, 0xc6, 0x18, 0x00,
- 0x8e, 0x00, 0xc6, 0x66, 0x60, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x00,
- 0x24, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x7e,
- 0x8e, 0x66, 0x18, 0x00, 0x18, 0x18, 0x00, 0x66, 0x00, 0x18, 0x00, 0x18, 0x00, 0xfe, 0x60, 0xac,
- 0x68, 0x30, 0x30, 0x0c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x78, 0x06, 0x06, 0x6c, 0x7c,
- 0x60, 0x06, 0x66, 0x66, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x06, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60,
- 0x60, 0x60, 0x66, 0x18, 0x06, 0xd8, 0x60, 0xfe, 0xf6, 0x66, 0x66, 0xcc, 0x66, 0x70, 0x18, 0x66,
- 0x66, 0xc6, 0x3c, 0x3c, 0x18, 0x30, 0x30, 0x0c, 0x6c, 0x00, 0x0c, 0x3c, 0x7c, 0x3c, 0x3e, 0x3c,
- 0x7c, 0x3e, 0x7c, 0x18, 0x0c, 0x66, 0x18, 0xec, 0x7c, 0x3c, 0x7c, 0x3e, 0x7c, 0x3c, 0x7c, 0x66,
- 0x66, 0xc6, 0xc6, 0x66, 0x7e, 0x18, 0x18, 0x18, 0x00, 0xf0, 0x66, 0x18, 0x3e, 0x30, 0x66, 0x3c,
- 0x18, 0x3c, 0x00, 0x9d, 0x44, 0x66, 0x00, 0xb9, 0x00, 0x3c, 0x7e, 0x18, 0x18, 0x60, 0x66, 0x7a,
- 0x18, 0x00, 0x30, 0x44, 0x66, 0x4c, 0x4c, 0x66, 0x18, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x60,
- 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x60, 0xd8, 0x3c, 0x60, 0x66, 0xc6, 0xe6, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x6c, 0x66, 0x6c, 0x66, 0x66, 0x66, 0x7e, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x18, 0x3c, 0x7e, 0x3c, 0x3e, 0x6c, 0x00, 0x18, 0x3c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x66, 0x1e, 0x3c, 0x66, 0x00, 0x7e, 0x7e, 0x00, 0x18, 0x00, 0x6c, 0x3c, 0xd8, 0x76, 0x00,
- 0x30, 0x0c, 0xff, 0x7e, 0x00, 0x7e, 0x00, 0x18, 0x7e, 0x18, 0x0c, 0x1c, 0xcc, 0x06, 0x7c, 0x0c,
- 0x3c, 0x3e, 0x00, 0x00, 0x60, 0x00, 0x06, 0x0c, 0xd6, 0x7e, 0x7c, 0x60, 0x66, 0x78, 0x78, 0x6e,
- 0x7e, 0x18, 0x06, 0xf0, 0x60, 0xd6, 0xde, 0x66, 0x7c, 0xcc, 0x7c, 0x3c, 0x18, 0x66, 0x66, 0xd6,
- 0x18, 0x18, 0x30, 0x30, 0x18, 0x0c, 0xc6, 0x00, 0x00, 0x06, 0x66, 0x60, 0x66, 0x66, 0x30, 0x66,
- 0x66, 0x18, 0x0c, 0x6c, 0x18, 0xfe, 0x66, 0x66, 0x66, 0x66, 0x66, 0x60, 0x30, 0x66, 0x66, 0xc6,
- 0x6c, 0x66, 0x0c, 0x70, 0x18, 0x0e, 0x00, 0xc3, 0x66, 0x18, 0x6c, 0x78, 0x3c, 0x18, 0x00, 0x66,
- 0x00, 0xb1, 0x3c, 0xcc, 0x00, 0xa5, 0x00, 0x00, 0x18, 0x30, 0x0c, 0x00, 0x66, 0x3a, 0x18, 0x00,
- 0x30, 0x38, 0x33, 0x58, 0x58, 0x2c, 0x30, 0x7e, 0x18, 0x66, 0x66, 0x66, 0x66, 0x78, 0x60, 0x66,
- 0x60, 0x4c, 0x60, 0x6e, 0xf0, 0x18, 0x60, 0x30, 0xe6, 0xf6, 0x66, 0x66, 0x66, 0x66, 0x38, 0x66,
- 0x70, 0x30, 0x66, 0x66, 0x4c, 0x4c, 0x6c, 0x06, 0x18, 0x06, 0x3c, 0x06, 0x06, 0x66, 0x66, 0x3c,
- 0x66, 0x0c, 0x66, 0x66, 0x78, 0x18, 0x18, 0x60, 0x7c, 0x66, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x66,
- 0x78, 0x60, 0x66, 0x66, 0x0c, 0x0c, 0x00, 0x18, 0x00, 0xfe, 0x06, 0x36, 0xdc, 0x00, 0x30, 0x0c,
- 0x3c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x76, 0x18, 0x18, 0x06, 0xfe, 0x06, 0x66, 0x18, 0x66, 0x06,
- 0x00, 0x00, 0x18, 0x7e, 0x18, 0x18, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60, 0x60, 0x66, 0x66, 0x18,
- 0x06, 0xd8, 0x60, 0xc6, 0xce, 0x66, 0x60, 0xcc, 0x6c, 0x0e, 0x18, 0x66, 0x3c, 0xfe, 0x3c, 0x18,
- 0x60, 0x30, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x60, 0x66, 0x7e, 0x30, 0x66, 0x66, 0x18,
- 0x0c, 0x78, 0x18, 0xd6, 0x66, 0x66, 0x66, 0x66, 0x60, 0x3c, 0x30, 0x66, 0x66, 0xd6, 0x38, 0x66,
- 0x18, 0x18, 0x18, 0x18, 0x00, 0x0f, 0x66, 0x18, 0x3e, 0x30, 0x42, 0x3c, 0x18, 0x3c, 0x00, 0x9d,
- 0x00, 0x66, 0x00, 0xb9, 0x00, 0x00, 0x18, 0x7c, 0x78, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x30, 0x00,
- 0x66, 0x32, 0x3e, 0xd9, 0x60, 0x66, 0x18, 0x7e, 0x40, 0x7e, 0x7e, 0x60, 0x78, 0x40, 0x78, 0x18,
- 0x78, 0x66, 0xd8, 0x18, 0x60, 0x0c, 0xf6, 0xde, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x66, 0xe0, 0x0c,
- 0x66, 0x66, 0x18, 0x18, 0x66, 0x3e, 0x18, 0x3e, 0x60, 0x3e, 0x3e, 0x7e, 0x7e, 0x60, 0x7e, 0x18,
- 0x7e, 0x66, 0x6c, 0x18, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x18, 0x3c,
- 0x66, 0x66, 0x18, 0x18, 0x00, 0x00, 0x00, 0x6c, 0x7c, 0x6a, 0xce, 0x00, 0x18, 0x18, 0x66, 0x18,
- 0x18, 0x00, 0x18, 0x60, 0x66, 0x18, 0x30, 0x66, 0x0c, 0x66, 0x66, 0x18, 0x66, 0x0c, 0x18, 0x18,
- 0x06, 0x00, 0x60, 0x00, 0xc0, 0x66, 0x66, 0x30, 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x66, 0xcc,
- 0x60, 0xc6, 0xc6, 0x66, 0x60, 0xdc, 0x66, 0x66, 0x18, 0x66, 0x3c, 0xee, 0x66, 0x18, 0xc0, 0x30,
- 0x06, 0x0c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x60, 0x66, 0x60, 0x30, 0x3e, 0x66, 0x18, 0x0c, 0x6c,
- 0x18, 0xc6, 0x66, 0x66, 0x7c, 0x3e, 0x60, 0x06, 0x30, 0x66, 0x3c, 0xfe, 0x6c, 0x3c, 0x30, 0x18,
- 0x18, 0x18, 0x00, 0x3c, 0x66, 0x18, 0x0c, 0x30, 0x00, 0x18, 0x18, 0x06, 0x00, 0x81, 0x7e, 0x33,
- 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0x66,
- 0x62, 0x33, 0x66, 0x66, 0x18, 0x66, 0x66, 0x66, 0x66, 0x60, 0x60, 0x66, 0x60, 0x32, 0x60, 0x3e,
- 0xcc, 0x18, 0x7e, 0x66, 0xde, 0xce, 0x66, 0x66, 0x66, 0x66, 0xc6, 0x66, 0x60, 0x66, 0x66, 0x66,
- 0x32, 0x32, 0x66, 0x66, 0x18, 0x66, 0x60, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x30, 0x60, 0x3e,
- 0x66, 0x18, 0x18, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x18, 0x66, 0x18, 0x06, 0x66, 0x66,
- 0x30, 0x30, 0x00, 0x18, 0x00, 0x6c, 0x18, 0xcc, 0x7b, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x18, 0x00,
- 0x18, 0xc0, 0x3c, 0x18, 0x7e, 0x3c, 0x0c, 0x3c, 0x3c, 0x18, 0x3c, 0x38, 0x18, 0x18, 0x00, 0x00,
- 0x00, 0x18, 0x78, 0x66, 0x7c, 0x1e, 0x78, 0x7e, 0x60, 0x3e, 0x66, 0x3c, 0x3c, 0xc6, 0x7e, 0xc6,
- 0xc6, 0x3c, 0x60, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0xc6, 0xc3, 0x18, 0xfe, 0x3c, 0x03, 0x3c,
- 0x00, 0x00, 0x00, 0x3e, 0x7c, 0x3c, 0x3e, 0x3c, 0x30, 0x06, 0x66, 0x0c, 0x0c, 0x66, 0x0c, 0xc6,
- 0x66, 0x3c, 0x60, 0x06, 0x60, 0x7c, 0x1c, 0x3e, 0x18, 0x6c, 0xc6, 0x18, 0x7e, 0x0e, 0x18, 0x70,
- 0x00, 0xf0, 0x7e, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x3c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x81,
- 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7f, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0xcf, 0xc4, 0x67,
- 0x3c, 0x67, 0x3e, 0x66, 0x3c, 0x66, 0x66, 0x7f, 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x18, 0x30, 0x3c,
- 0x18, 0x3c, 0xce, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x7e, 0x3c, 0x3c, 0x3c, 0x7e, 0x7e,
- 0x6c, 0x3f, 0x1e, 0x3e, 0x3c, 0x3e, 0x3e, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x3c, 0x06, 0x18, 0x0c,
- 0x0c, 0x7c, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x0c, 0x7c, 0x3e, 0x3e, 0x7e, 0x7e,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x01, 0x00, 0x03,
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x00, 0x30, 0x00,
- 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03,
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x18, 0x00,
- 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x08, 0x00, 0x18, 0x00, 0x08, 0x00, 0x20,
- 0x00, 0x08, 0x00, 0x28, 0x00, 0x08, 0x00, 0x30, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x00, 0x40,
- 0x00, 0x08, 0x00, 0x48, 0x00, 0x08, 0x00, 0x50, 0x00, 0x08, 0x00, 0x58, 0x00, 0x08, 0x00, 0x60,
- 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x00, 0x70, 0x00, 0x08, 0x00, 0x78, 0x00, 0x08, 0x00, 0x80,
- 0x00, 0x08, 0x00, 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0x98, 0x00, 0x08, 0x00, 0xa0,
- 0x00, 0x08, 0x00, 0xa8, 0x00, 0x08, 0x00, 0xb0, 0x00, 0x08, 0x00, 0xb8, 0x00, 0x08, 0x00, 0xc0,
- 0x00, 0x08, 0x00, 0xc8, 0x00, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x00, 0xd8, 0x00, 0x08, 0x00, 0xe0,
- 0x00, 0x08, 0x00, 0xe8, 0x00, 0x08, 0x00, 0xf0, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x08, 0x01, 0x00,
- 0x00, 0x08, 0x01, 0x08, 0x00, 0x08, 0x01, 0x10, 0x00, 0x08, 0x01, 0x18, 0x00, 0x08, 0x01, 0x20,
- 0x00, 0x08, 0x01, 0x28, 0x00, 0x08, 0x01, 0x30, 0x00, 0x08, 0x01, 0x38, 0x00, 0x08, 0x01, 0x40,
- 0x00, 0x08, 0x01, 0x48, 0x00, 0x08, 0x01, 0x50, 0x00, 0x08, 0x01, 0x58, 0x00, 0x08, 0x01, 0x60,
- 0x00, 0x08, 0x01, 0x68, 0x00, 0x08, 0x01, 0x70, 0x00, 0x08, 0x01, 0x78, 0x00, 0x08, 0x01, 0x80,
- 0x00, 0x08, 0x01, 0x88, 0x00, 0x08, 0x01, 0x90, 0x00, 0x08, 0x01, 0x98, 0x00, 0x08, 0x01, 0xa0,
- 0x00, 0x08, 0x01, 0xa8, 0x00, 0x08, 0x01, 0xb0, 0x00, 0x08, 0x01, 0xb8, 0x00, 0x08, 0x01, 0xc0,
- 0x00, 0x08, 0x01, 0xc8, 0x00, 0x08, 0x01, 0xd0, 0x00, 0x08, 0x01, 0xd8, 0x00, 0x08, 0x01, 0xe0,
- 0x00, 0x08, 0x01, 0xe8, 0x00, 0x08, 0x01, 0xf0, 0x00, 0x08, 0x01, 0xf8, 0x00, 0x08, 0x02, 0x00,
- 0x00, 0x08, 0x02, 0x08, 0x00, 0x08, 0x02, 0x10, 0x00, 0x08, 0x02, 0x18, 0x00, 0x08, 0x02, 0x20,
- 0x00, 0x08, 0x02, 0x28, 0x00, 0x08, 0x02, 0x30, 0x00, 0x08, 0x02, 0x38, 0x00, 0x08, 0x02, 0x40,
- 0x00, 0x08, 0x02, 0x48, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0x60,
- 0x00, 0x08, 0x02, 0x68, 0x00, 0x08, 0x02, 0x70, 0x00, 0x08, 0x02, 0x78, 0x00, 0x08, 0x02, 0x80,
- 0x00, 0x08, 0x02, 0x88, 0x00, 0x08, 0x02, 0x90, 0x00, 0x08, 0x02, 0x98, 0x00, 0x08, 0x02, 0xa0,
- 0x00, 0x08, 0x02, 0xa8, 0x00, 0x08, 0x02, 0xb0, 0x00, 0x08, 0x02, 0xb8, 0x00, 0x08, 0x02, 0xc0,
- 0x00, 0x08, 0x02, 0xc8, 0x00, 0x08, 0x02, 0xd0, 0x00, 0x08, 0x02, 0xd8, 0x00, 0x08, 0x02, 0xe0,
- 0x00, 0x08, 0x02, 0xe8, 0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00,
- 0x00, 0x08, 0x03, 0x08, 0x00, 0x08, 0x03, 0x10, 0x00, 0x08, 0x03, 0x18, 0x00, 0x08, 0x03, 0x20,
- 0x00, 0x08, 0x03, 0x28, 0x00, 0x08, 0x03, 0x30, 0x00, 0x08, 0x03, 0x38, 0x00, 0x08, 0x03, 0x40,
- 0x00, 0x08, 0x03, 0x48, 0x00, 0x08, 0x03, 0x50, 0x00, 0x08, 0x03, 0x58, 0x00, 0x08, 0x03, 0x60,
- 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x03, 0x68, 0x00, 0x08, 0x03, 0x70, 0x00, 0x08, 0x03, 0x78,
- 0x00, 0x08, 0x03, 0x80, 0x00, 0x08, 0x03, 0x88, 0x00, 0x08, 0x03, 0x90, 0x00, 0x08, 0x03, 0x98,
- 0x00, 0x08, 0x03, 0xa0, 0x00, 0x08, 0x03, 0xa8, 0x00, 0x08, 0x03, 0xb0, 0x00, 0x08, 0x03, 0xb8,
- 0x00, 0x08, 0x03, 0xc0, 0x00, 0x08, 0x03, 0xc8, 0x00, 0x08, 0x03, 0xd0, 0x00, 0x08, 0x03, 0xd8,
- 0x00, 0x08, 0x03, 0xe0, 0x00, 0x08, 0x03, 0xe8, 0x00, 0x08, 0x03, 0xf0, 0x00, 0x08, 0x03, 0xf8,
- 0x00, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x08, 0x00, 0x08, 0x04, 0x10, 0x00, 0x08, 0x04, 0x18,
- 0x00, 0x08, 0x04, 0x20, 0x00, 0x08, 0x04, 0x28, 0x00, 0x08, 0x04, 0x30, 0x00, 0x08, 0x04, 0x38,
- 0x00, 0x08, 0x04, 0x40, 0x00, 0x08, 0x04, 0x48, 0x00, 0x08, 0x04, 0x50, 0x00, 0x08, 0x04, 0x58,
- 0x00, 0x08, 0x04, 0x60, 0x00, 0x08, 0x04, 0x68, 0x00, 0x08, 0x04, 0x70, 0x00, 0x08, 0x04, 0x78,
- 0x00, 0x08, 0x04, 0x80, 0x00, 0x08, 0x04, 0x88, 0x00, 0x08, 0x04, 0x90, 0x00, 0x08, 0x04, 0x98,
- 0x00, 0x08, 0x04, 0xa0, 0x00, 0x08, 0x04, 0xa8, 0x00, 0x08, 0x04, 0xb0, 0x00, 0x08, 0x04, 0xb8,
- 0x00, 0x08, 0x04, 0xc0, 0x00, 0x08, 0x04, 0xc8, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd8,
- 0x00, 0x08, 0x04, 0xe0, 0x00, 0x08, 0x04, 0xe8, 0x00, 0x08, 0x04, 0xf0, 0x00, 0x08, 0x04, 0xf8,
- 0x00, 0x08, 0x05, 0x00, 0x00, 0x08, 0x05, 0x08, 0x00, 0x08, 0x05, 0x10, 0x00, 0x08, 0x05, 0x18,
- 0x00, 0x08, 0x05, 0x20, 0x00, 0x08, 0x05, 0x28, 0x00, 0x08, 0x05, 0x30, 0x00, 0x08, 0x05, 0x38,
- 0x00, 0x08, 0x05, 0x40, 0x00, 0x08, 0x05, 0x48, 0x00, 0x08, 0x05, 0x50, 0x00, 0x08, 0x05, 0x58,
- 0x00, 0x08, 0x05, 0x60, 0x00, 0x08, 0x05, 0x68, 0x00, 0x08, 0x05, 0x70, 0x00, 0x08, 0x05, 0x78,
- 0x00, 0x08, 0x05, 0x80, 0x00, 0x08, 0x05, 0x88, 0x00, 0x08, 0x05, 0x90, 0x00, 0x08, 0x05, 0x98,
- 0x00, 0x08, 0x05, 0xa0, 0x00, 0x08, 0x05, 0xa8, 0x00, 0x08, 0x05, 0xb0, 0x00, 0x08, 0x05, 0xb8,
- 0x00, 0x08, 0x05, 0xc0, 0x00, 0x08, 0x05, 0xc8, 0x00, 0x08, 0x05, 0xd0, 0x00, 0x08, 0x05, 0xd8,
- 0x00, 0x08, 0x05, 0xe0, 0x00, 0x08, 0x05, 0xe8, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf2
+ 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79,
+ 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x1a, 0x0f, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x45, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x74, 0x00, 0x08,
+ 0x00, 0x40, 0x00, 0x08, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x20, 0xff, 0x00, 0x00, 0x00, 0x6e,
+ 0x00, 0xbe, 0x00, 0x00, 0x06, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x6c, 0x6c, 0x18, 0x00, 0x38, 0x18, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, 0x18,
+ 0x3c, 0x3c, 0x1c, 0x7e, 0x1c, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x3c,
+ 0x7c, 0x1e, 0x78, 0x7e, 0x7e, 0x3c, 0x66, 0x3c, 0x06, 0xc6, 0x60, 0xc6, 0xc6, 0x3c, 0x7c, 0x78,
+ 0x7c, 0x3c, 0x7e, 0x66, 0x66, 0xc6, 0xc3, 0xc3, 0xfe, 0x3c, 0xc0, 0x3c, 0x10, 0x00, 0x18, 0x00,
+ 0x60, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x60, 0x18, 0x0c, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x70, 0x72, 0x0f, 0x00, 0x18,
+ 0x00, 0x1c, 0x42, 0xc3, 0x18, 0x3c, 0x66, 0x7e, 0x1c, 0x00, 0x3e, 0x7e, 0x7e, 0x3c, 0x18, 0x78,
+ 0x78, 0x18, 0x00, 0x3e, 0x00, 0x00, 0x30, 0x38, 0x00, 0x40, 0x40, 0xc0, 0x18, 0x3c, 0x3c, 0x7e,
+ 0x06, 0x66, 0x18, 0x7e, 0x7e, 0x36, 0x0c, 0x0c, 0x18, 0x3c, 0xc6, 0x3c, 0x60, 0x76, 0x18, 0x00,
+ 0x0c, 0x7e, 0x71, 0x66, 0x00, 0x66, 0x60, 0x0e, 0x7e, 0x66, 0x18, 0x6e, 0x3c, 0x00, 0x18, 0x7e,
+ 0x06, 0x66, 0x18, 0x00, 0x7e, 0x34, 0x0c, 0x0c, 0x18, 0x0c, 0x60, 0x00, 0x18, 0x3c, 0x0c, 0x00,
+ 0x0c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x7e, 0x00, 0x18, 0x3c, 0x00, 0x18, 0x6c, 0x6c,
+ 0x3e, 0x66, 0x6c, 0x18, 0x18, 0x18, 0x66, 0x18, 0x00, 0x00, 0x00, 0x06, 0x66, 0x38, 0x66, 0x66,
+ 0x3c, 0x60, 0x30, 0x06, 0x66, 0x66, 0x18, 0x18, 0x06, 0x00, 0x60, 0x66, 0xc6, 0x66, 0x66, 0x30,
+ 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x06, 0xcc, 0x60, 0xee, 0xe6, 0x66, 0x66, 0xcc, 0x66, 0x66,
+ 0x18, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x0c, 0x30, 0x60, 0x0c, 0x38, 0x00, 0x18, 0x00, 0x60, 0x00,
+ 0x06, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x9c, 0x3c, 0x7e, 0x00, 0x0c, 0x36,
+ 0x3c, 0x66, 0x18, 0x60, 0x66, 0x81, 0x24, 0x33, 0x06, 0x81, 0x00, 0x66, 0x18, 0x0c, 0x0c, 0x30,
+ 0x00, 0x7a, 0x00, 0x00, 0x70, 0x44, 0xcc, 0xc6, 0xc6, 0x23, 0x00, 0x66, 0x18, 0x00, 0x1c, 0x00,
+ 0x24, 0x60, 0x00, 0x1c, 0x18, 0x18, 0x00, 0x66, 0xcc, 0x00, 0x60, 0x3c, 0x30, 0xc6, 0x18, 0x00,
+ 0x8e, 0x00, 0xc6, 0x66, 0x60, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x00,
+ 0x24, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x7e,
+ 0x8e, 0x66, 0x18, 0x00, 0x18, 0x18, 0x00, 0x66, 0x00, 0x18, 0x00, 0x18, 0x00, 0xfe, 0x60, 0xac,
+ 0x68, 0x30, 0x30, 0x0c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x78, 0x06, 0x06, 0x6c, 0x7c,
+ 0x60, 0x06, 0x66, 0x66, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x06, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60,
+ 0x60, 0x60, 0x66, 0x18, 0x06, 0xd8, 0x60, 0xfe, 0xf6, 0x66, 0x66, 0xcc, 0x66, 0x70, 0x18, 0x66,
+ 0x66, 0xc6, 0x3c, 0x3c, 0x18, 0x30, 0x30, 0x0c, 0x6c, 0x00, 0x0c, 0x3c, 0x7c, 0x3c, 0x3e, 0x3c,
+ 0x7c, 0x3e, 0x7c, 0x18, 0x0c, 0x66, 0x18, 0xec, 0x7c, 0x3c, 0x7c, 0x3e, 0x7c, 0x3c, 0x7c, 0x66,
+ 0x66, 0xc6, 0xc6, 0x66, 0x7e, 0x18, 0x18, 0x18, 0x00, 0xf0, 0x66, 0x18, 0x3e, 0x30, 0x66, 0x3c,
+ 0x18, 0x3c, 0x00, 0x9d, 0x44, 0x66, 0x00, 0xb9, 0x00, 0x3c, 0x7e, 0x18, 0x18, 0x60, 0x66, 0x7a,
+ 0x18, 0x00, 0x30, 0x44, 0x66, 0x4c, 0x4c, 0x66, 0x18, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x60,
+ 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x60, 0xd8, 0x3c, 0x60, 0x66, 0xc6, 0xe6, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x6c, 0x66, 0x6c, 0x66, 0x66, 0x66, 0x7e, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x18, 0x3c, 0x7e, 0x3c, 0x3e, 0x6c, 0x00, 0x18, 0x3c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x1e, 0x3c, 0x66, 0x00, 0x7e, 0x7e, 0x00, 0x18, 0x00, 0x6c, 0x3c, 0xd8, 0x76, 0x00,
+ 0x30, 0x0c, 0xff, 0x7e, 0x00, 0x7e, 0x00, 0x18, 0x7e, 0x18, 0x0c, 0x1c, 0xcc, 0x06, 0x7c, 0x0c,
+ 0x3c, 0x3e, 0x00, 0x00, 0x60, 0x00, 0x06, 0x0c, 0xd6, 0x7e, 0x7c, 0x60, 0x66, 0x78, 0x78, 0x6e,
+ 0x7e, 0x18, 0x06, 0xf0, 0x60, 0xd6, 0xde, 0x66, 0x7c, 0xcc, 0x7c, 0x3c, 0x18, 0x66, 0x66, 0xd6,
+ 0x18, 0x18, 0x30, 0x30, 0x18, 0x0c, 0xc6, 0x00, 0x00, 0x06, 0x66, 0x60, 0x66, 0x66, 0x30, 0x66,
+ 0x66, 0x18, 0x0c, 0x6c, 0x18, 0xfe, 0x66, 0x66, 0x66, 0x66, 0x66, 0x60, 0x30, 0x66, 0x66, 0xc6,
+ 0x6c, 0x66, 0x0c, 0x70, 0x18, 0x0e, 0x00, 0xc3, 0x66, 0x18, 0x6c, 0x78, 0x3c, 0x18, 0x00, 0x66,
+ 0x00, 0xb1, 0x3c, 0xcc, 0x00, 0xa5, 0x00, 0x00, 0x18, 0x30, 0x0c, 0x00, 0x66, 0x3a, 0x18, 0x00,
+ 0x30, 0x38, 0x33, 0x58, 0x58, 0x2c, 0x30, 0x7e, 0x18, 0x66, 0x66, 0x66, 0x66, 0x78, 0x60, 0x66,
+ 0x60, 0x4c, 0x60, 0x6e, 0xf0, 0x18, 0x60, 0x30, 0xe6, 0xf6, 0x66, 0x66, 0x66, 0x66, 0x38, 0x66,
+ 0x70, 0x30, 0x66, 0x66, 0x4c, 0x4c, 0x6c, 0x06, 0x18, 0x06, 0x3c, 0x06, 0x06, 0x66, 0x66, 0x3c,
+ 0x66, 0x0c, 0x66, 0x66, 0x78, 0x18, 0x18, 0x60, 0x7c, 0x66, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x66,
+ 0x78, 0x60, 0x66, 0x66, 0x0c, 0x0c, 0x00, 0x18, 0x00, 0xfe, 0x06, 0x36, 0xdc, 0x00, 0x30, 0x0c,
+ 0x3c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x76, 0x18, 0x18, 0x06, 0xfe, 0x06, 0x66, 0x18, 0x66, 0x06,
+ 0x00, 0x00, 0x18, 0x7e, 0x18, 0x18, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60, 0x60, 0x66, 0x66, 0x18,
+ 0x06, 0xd8, 0x60, 0xc6, 0xce, 0x66, 0x60, 0xcc, 0x6c, 0x0e, 0x18, 0x66, 0x3c, 0xfe, 0x3c, 0x18,
+ 0x60, 0x30, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x60, 0x66, 0x7e, 0x30, 0x66, 0x66, 0x18,
+ 0x0c, 0x78, 0x18, 0xd6, 0x66, 0x66, 0x66, 0x66, 0x60, 0x3c, 0x30, 0x66, 0x66, 0xd6, 0x38, 0x66,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x0f, 0x66, 0x18, 0x3e, 0x30, 0x42, 0x3c, 0x18, 0x3c, 0x00, 0x9d,
+ 0x00, 0x66, 0x00, 0xb9, 0x00, 0x00, 0x18, 0x7c, 0x78, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x30, 0x00,
+ 0x66, 0x32, 0x3e, 0xd9, 0x60, 0x66, 0x18, 0x7e, 0x40, 0x7e, 0x7e, 0x60, 0x78, 0x40, 0x78, 0x18,
+ 0x78, 0x66, 0xd8, 0x18, 0x60, 0x0c, 0xf6, 0xde, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x66, 0xe0, 0x0c,
+ 0x66, 0x66, 0x18, 0x18, 0x66, 0x3e, 0x18, 0x3e, 0x60, 0x3e, 0x3e, 0x7e, 0x7e, 0x60, 0x7e, 0x18,
+ 0x7e, 0x66, 0x6c, 0x18, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x18, 0x3c,
+ 0x66, 0x66, 0x18, 0x18, 0x00, 0x00, 0x00, 0x6c, 0x7c, 0x6a, 0xce, 0x00, 0x18, 0x18, 0x66, 0x18,
+ 0x18, 0x00, 0x18, 0x60, 0x66, 0x18, 0x30, 0x66, 0x0c, 0x66, 0x66, 0x18, 0x66, 0x0c, 0x18, 0x18,
+ 0x06, 0x00, 0x60, 0x00, 0xc0, 0x66, 0x66, 0x30, 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x66, 0xcc,
+ 0x60, 0xc6, 0xc6, 0x66, 0x60, 0xdc, 0x66, 0x66, 0x18, 0x66, 0x3c, 0xee, 0x66, 0x18, 0xc0, 0x30,
+ 0x06, 0x0c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x60, 0x66, 0x60, 0x30, 0x3e, 0x66, 0x18, 0x0c, 0x6c,
+ 0x18, 0xc6, 0x66, 0x66, 0x7c, 0x3e, 0x60, 0x06, 0x30, 0x66, 0x3c, 0xfe, 0x6c, 0x3c, 0x30, 0x18,
+ 0x18, 0x18, 0x00, 0x3c, 0x66, 0x18, 0x0c, 0x30, 0x00, 0x18, 0x18, 0x06, 0x00, 0x81, 0x7e, 0x33,
+ 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0x66,
+ 0x62, 0x33, 0x66, 0x66, 0x18, 0x66, 0x66, 0x66, 0x66, 0x60, 0x60, 0x66, 0x60, 0x32, 0x60, 0x3e,
+ 0xcc, 0x18, 0x7e, 0x66, 0xde, 0xce, 0x66, 0x66, 0x66, 0x66, 0xc6, 0x66, 0x60, 0x66, 0x66, 0x66,
+ 0x32, 0x32, 0x66, 0x66, 0x18, 0x66, 0x60, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x30, 0x60, 0x3e,
+ 0x66, 0x18, 0x18, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x18, 0x66, 0x18, 0x06, 0x66, 0x66,
+ 0x30, 0x30, 0x00, 0x18, 0x00, 0x6c, 0x18, 0xcc, 0x7b, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x18, 0x00,
+ 0x18, 0xc0, 0x3c, 0x18, 0x7e, 0x3c, 0x0c, 0x3c, 0x3c, 0x18, 0x3c, 0x38, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x18, 0x78, 0x66, 0x7c, 0x1e, 0x78, 0x7e, 0x60, 0x3e, 0x66, 0x3c, 0x3c, 0xc6, 0x7e, 0xc6,
+ 0xc6, 0x3c, 0x60, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0xc6, 0xc3, 0x18, 0xfe, 0x3c, 0x03, 0x3c,
+ 0x00, 0x00, 0x00, 0x3e, 0x7c, 0x3c, 0x3e, 0x3c, 0x30, 0x06, 0x66, 0x0c, 0x0c, 0x66, 0x0c, 0xc6,
+ 0x66, 0x3c, 0x60, 0x06, 0x60, 0x7c, 0x1c, 0x3e, 0x18, 0x6c, 0xc6, 0x18, 0x7e, 0x0e, 0x18, 0x70,
+ 0x00, 0xf0, 0x7e, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x3c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x81,
+ 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7f, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0xcf, 0xc4, 0x67,
+ 0x3c, 0x67, 0x3e, 0x66, 0x3c, 0x66, 0x66, 0x7f, 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x18, 0x30, 0x3c,
+ 0x18, 0x3c, 0xce, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x7e, 0x3c, 0x3c, 0x3c, 0x7e, 0x7e,
+ 0x6c, 0x3f, 0x1e, 0x3e, 0x3c, 0x3e, 0x3e, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x3c, 0x06, 0x18, 0x0c,
+ 0x0c, 0x7c, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x0c, 0x7c, 0x3e, 0x3e, 0x7e, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x01, 0x00, 0x03,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x00, 0x30, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x18, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x08, 0x00, 0x18, 0x00, 0x08, 0x00, 0x20,
+ 0x00, 0x08, 0x00, 0x28, 0x00, 0x08, 0x00, 0x30, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x00, 0x40,
+ 0x00, 0x08, 0x00, 0x48, 0x00, 0x08, 0x00, 0x50, 0x00, 0x08, 0x00, 0x58, 0x00, 0x08, 0x00, 0x60,
+ 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x00, 0x70, 0x00, 0x08, 0x00, 0x78, 0x00, 0x08, 0x00, 0x80,
+ 0x00, 0x08, 0x00, 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0x98, 0x00, 0x08, 0x00, 0xa0,
+ 0x00, 0x08, 0x00, 0xa8, 0x00, 0x08, 0x00, 0xb0, 0x00, 0x08, 0x00, 0xb8, 0x00, 0x08, 0x00, 0xc0,
+ 0x00, 0x08, 0x00, 0xc8, 0x00, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x00, 0xd8, 0x00, 0x08, 0x00, 0xe0,
+ 0x00, 0x08, 0x00, 0xe8, 0x00, 0x08, 0x00, 0xf0, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x08, 0x01, 0x00,
+ 0x00, 0x08, 0x01, 0x08, 0x00, 0x08, 0x01, 0x10, 0x00, 0x08, 0x01, 0x18, 0x00, 0x08, 0x01, 0x20,
+ 0x00, 0x08, 0x01, 0x28, 0x00, 0x08, 0x01, 0x30, 0x00, 0x08, 0x01, 0x38, 0x00, 0x08, 0x01, 0x40,
+ 0x00, 0x08, 0x01, 0x48, 0x00, 0x08, 0x01, 0x50, 0x00, 0x08, 0x01, 0x58, 0x00, 0x08, 0x01, 0x60,
+ 0x00, 0x08, 0x01, 0x68, 0x00, 0x08, 0x01, 0x70, 0x00, 0x08, 0x01, 0x78, 0x00, 0x08, 0x01, 0x80,
+ 0x00, 0x08, 0x01, 0x88, 0x00, 0x08, 0x01, 0x90, 0x00, 0x08, 0x01, 0x98, 0x00, 0x08, 0x01, 0xa0,
+ 0x00, 0x08, 0x01, 0xa8, 0x00, 0x08, 0x01, 0xb0, 0x00, 0x08, 0x01, 0xb8, 0x00, 0x08, 0x01, 0xc0,
+ 0x00, 0x08, 0x01, 0xc8, 0x00, 0x08, 0x01, 0xd0, 0x00, 0x08, 0x01, 0xd8, 0x00, 0x08, 0x01, 0xe0,
+ 0x00, 0x08, 0x01, 0xe8, 0x00, 0x08, 0x01, 0xf0, 0x00, 0x08, 0x01, 0xf8, 0x00, 0x08, 0x02, 0x00,
+ 0x00, 0x08, 0x02, 0x08, 0x00, 0x08, 0x02, 0x10, 0x00, 0x08, 0x02, 0x18, 0x00, 0x08, 0x02, 0x20,
+ 0x00, 0x08, 0x02, 0x28, 0x00, 0x08, 0x02, 0x30, 0x00, 0x08, 0x02, 0x38, 0x00, 0x08, 0x02, 0x40,
+ 0x00, 0x08, 0x02, 0x48, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0x60,
+ 0x00, 0x08, 0x02, 0x68, 0x00, 0x08, 0x02, 0x70, 0x00, 0x08, 0x02, 0x78, 0x00, 0x08, 0x02, 0x80,
+ 0x00, 0x08, 0x02, 0x88, 0x00, 0x08, 0x02, 0x90, 0x00, 0x08, 0x02, 0x98, 0x00, 0x08, 0x02, 0xa0,
+ 0x00, 0x08, 0x02, 0xa8, 0x00, 0x08, 0x02, 0xb0, 0x00, 0x08, 0x02, 0xb8, 0x00, 0x08, 0x02, 0xc0,
+ 0x00, 0x08, 0x02, 0xc8, 0x00, 0x08, 0x02, 0xd0, 0x00, 0x08, 0x02, 0xd8, 0x00, 0x08, 0x02, 0xe0,
+ 0x00, 0x08, 0x02, 0xe8, 0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x08, 0x03, 0x08, 0x00, 0x08, 0x03, 0x10, 0x00, 0x08, 0x03, 0x18, 0x00, 0x08, 0x03, 0x20,
+ 0x00, 0x08, 0x03, 0x28, 0x00, 0x08, 0x03, 0x30, 0x00, 0x08, 0x03, 0x38, 0x00, 0x08, 0x03, 0x40,
+ 0x00, 0x08, 0x03, 0x48, 0x00, 0x08, 0x03, 0x50, 0x00, 0x08, 0x03, 0x58, 0x00, 0x08, 0x03, 0x60,
+ 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x03, 0x68, 0x00, 0x08, 0x03, 0x70, 0x00, 0x08, 0x03, 0x78,
+ 0x00, 0x08, 0x03, 0x80, 0x00, 0x08, 0x03, 0x88, 0x00, 0x08, 0x03, 0x90, 0x00, 0x08, 0x03, 0x98,
+ 0x00, 0x08, 0x03, 0xa0, 0x00, 0x08, 0x03, 0xa8, 0x00, 0x08, 0x03, 0xb0, 0x00, 0x08, 0x03, 0xb8,
+ 0x00, 0x08, 0x03, 0xc0, 0x00, 0x08, 0x03, 0xc8, 0x00, 0x08, 0x03, 0xd0, 0x00, 0x08, 0x03, 0xd8,
+ 0x00, 0x08, 0x03, 0xe0, 0x00, 0x08, 0x03, 0xe8, 0x00, 0x08, 0x03, 0xf0, 0x00, 0x08, 0x03, 0xf8,
+ 0x00, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x08, 0x00, 0x08, 0x04, 0x10, 0x00, 0x08, 0x04, 0x18,
+ 0x00, 0x08, 0x04, 0x20, 0x00, 0x08, 0x04, 0x28, 0x00, 0x08, 0x04, 0x30, 0x00, 0x08, 0x04, 0x38,
+ 0x00, 0x08, 0x04, 0x40, 0x00, 0x08, 0x04, 0x48, 0x00, 0x08, 0x04, 0x50, 0x00, 0x08, 0x04, 0x58,
+ 0x00, 0x08, 0x04, 0x60, 0x00, 0x08, 0x04, 0x68, 0x00, 0x08, 0x04, 0x70, 0x00, 0x08, 0x04, 0x78,
+ 0x00, 0x08, 0x04, 0x80, 0x00, 0x08, 0x04, 0x88, 0x00, 0x08, 0x04, 0x90, 0x00, 0x08, 0x04, 0x98,
+ 0x00, 0x08, 0x04, 0xa0, 0x00, 0x08, 0x04, 0xa8, 0x00, 0x08, 0x04, 0xb0, 0x00, 0x08, 0x04, 0xb8,
+ 0x00, 0x08, 0x04, 0xc0, 0x00, 0x08, 0x04, 0xc8, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd8,
+ 0x00, 0x08, 0x04, 0xe0, 0x00, 0x08, 0x04, 0xe8, 0x00, 0x08, 0x04, 0xf0, 0x00, 0x08, 0x04, 0xf8,
+ 0x00, 0x08, 0x05, 0x00, 0x00, 0x08, 0x05, 0x08, 0x00, 0x08, 0x05, 0x10, 0x00, 0x08, 0x05, 0x18,
+ 0x00, 0x08, 0x05, 0x20, 0x00, 0x08, 0x05, 0x28, 0x00, 0x08, 0x05, 0x30, 0x00, 0x08, 0x05, 0x38,
+ 0x00, 0x08, 0x05, 0x40, 0x00, 0x08, 0x05, 0x48, 0x00, 0x08, 0x05, 0x50, 0x00, 0x08, 0x05, 0x58,
+ 0x00, 0x08, 0x05, 0x60, 0x00, 0x08, 0x05, 0x68, 0x00, 0x08, 0x05, 0x70, 0x00, 0x08, 0x05, 0x78,
+ 0x00, 0x08, 0x05, 0x80, 0x00, 0x08, 0x05, 0x88, 0x00, 0x08, 0x05, 0x90, 0x00, 0x08, 0x05, 0x98,
+ 0x00, 0x08, 0x05, 0xa0, 0x00, 0x08, 0x05, 0xa8, 0x00, 0x08, 0x05, 0xb0, 0x00, 0x08, 0x05, 0xb8,
+ 0x00, 0x08, 0x05, 0xc0, 0x00, 0x08, 0x05, 0xc8, 0x00, 0x08, 0x05, 0xd0, 0x00, 0x08, 0x05, 0xd8,
+ 0x00, 0x08, 0x05, 0xe0, 0x00, 0x08, 0x05, 0xe8, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf2
};
diff --git a/engines/queen/debug.cpp b/engines/queen/debug.cpp
index 1abb2415d2..f4d7cf3b2e 100644
--- a/engines/queen/debug.cpp
+++ b/engines/queen/debug.cpp
@@ -149,12 +149,12 @@ bool Debugger::Cmd_Items(int argc, const char **argv) {
bool Debugger::Cmd_PrintBobs(int argc, const char**argv) {
int i;
BobSlot *bob = _vm->graphics()->bob(0);
- DebugPrintf("+--------------------------------+\n");
- DebugPrintf("|# | x| y|f|scl|frm|a|m| ex| ey|\n");
- DebugPrintf("+--+---+---+-+---+---+-+-+---+---+\n");
+ DebugPrintf("+------------------------------------+\n");
+ DebugPrintf("|# | x| y|f|scl|frm|a|m|spd| ex| ey|\n");
+ DebugPrintf("+--+---+---+-+---+---+-+-+---+---+---+\n");
for (i = 0; i < Graphics::MAX_BOBS_NUMBER; ++i, ++bob) {
if (bob->active) {
- DebugPrintf("|%2d|%3d|%3d|%1d|%3d|%3d|%1d|%1d|%3d|%3d|\n",
+ DebugPrintf("|%2d|%3d|%3d|%1d|%3d|%3d|%1d|%1d|%3d|%3d|%3d|\n",
i, bob->x, bob->y, bob->xflip, bob->scale, bob->frameNum,
bob->animating, bob->moving, bob->speed, bob->endx, bob->endy);
}
diff --git a/engines/queen/logic.cpp b/engines/queen/logic.cpp
index 053312c584..de254300b6 100644
--- a/engines/queen/logic.cpp
+++ b/engines/queen/logic.cpp
@@ -1550,7 +1550,7 @@ void Logic::asmEndGame() {
while (n--) {
_vm->update();
}
-// printf("Game completed.");
+// debug("Game completed.");
_vm->quitGame();
}
@@ -2004,7 +2004,7 @@ void Logic::asmPanLeftToBomb() {
}
void Logic::asmEndDemo() {
-// printf("Flight of the Amazon Queen, released January 95.");
+// debug("Flight of the Amazon Queen, released January 95.");
_vm->quitGame();
}
@@ -2049,7 +2049,7 @@ void Logic::asmInterviewIntro() {
}
void Logic::asmEndInterview() {
-// printf("Interactive Interview copyright (c) 1995, IBI.");
+// debug("Interactive Interview copyright (c) 1995, IBI.");
_vm->quitGame();
}
diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp
index 0498fc426a..904952b1c5 100644
--- a/engines/queen/midiadlib.cpp
+++ b/engines/queen/midiadlib.cpp
@@ -35,7 +35,7 @@ class AdLibMidiChannel;
class AdLibMidiDriver : public MidiDriver_Emulated {
public:
- AdLibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) {}
+ AdLibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { _adlibWaveformSelect = 0; }
~AdLibMidiDriver() {}
// MidiDriver
diff --git a/engines/queen/queen.h b/engines/queen/queen.h
index 5a8f1357f4..93d705b182 100644
--- a/engines/queen/queen.h
+++ b/engines/queen/queen.h
@@ -56,8 +56,8 @@ FORCEINLINE int16 READ_BE_INT16(const void *ptr) {
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Flight of the Amazon Queen
*/
namespace Queen {
diff --git a/engines/queen/resource.cpp b/engines/queen/resource.cpp
index 1e2eff8458..a70d1f1613 100644
--- a/engines/queen/resource.cpp
+++ b/engines/queen/resource.cpp
@@ -26,6 +26,7 @@
#include "common/debug.h"
#include "common/endian.h"
#include "common/config-manager.h"
+#include "common/substream.h"
#include "queen/resource.h"
namespace Queen {
diff --git a/engines/queen/sound.cpp b/engines/queen/sound.cpp
index 659da2dd97..10ffd61724 100644
--- a/engines/queen/sound.cpp
+++ b/engines/queen/sound.cpp
@@ -26,7 +26,7 @@
#include "common/config-manager.h"
#include "common/endian.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "queen/sound.h"
#include "queen/input.h"
@@ -119,7 +119,7 @@ public:
MP3Sound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) {
- Common::MemoryReadStream *tmp = f->readStream(size);
+ Common::SeekableReadStream *tmp = f->readStream(size);
assert(tmp);
_mixer->playStream(Audio::Mixer::kSFXSoundType, soundHandle, new AudioStreamWrapper(Audio::makeMP3Stream(tmp, DisposeAfterUse::YES)));
}
@@ -132,7 +132,7 @@ public:
OGGSound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) {
- Common::MemoryReadStream *tmp = f->readStream(size);
+ Common::SeekableReadStream *tmp = f->readStream(size);
assert(tmp);
_mixer->playStream(Audio::Mixer::kSFXSoundType, soundHandle, new AudioStreamWrapper(Audio::makeVorbisStream(tmp, DisposeAfterUse::YES)));
}
@@ -145,7 +145,7 @@ public:
FLACSound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) {
- Common::MemoryReadStream *tmp = f->readStream(size);
+ Common::SeekableReadStream *tmp = f->readStream(size);
assert(tmp);
_mixer->playStream(Audio::Mixer::kSFXSoundType, soundHandle, new AudioStreamWrapper(Audio::makeFLACStream(tmp, DisposeAfterUse::YES)));
}
diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp
index 6bc79daa73..106b0d6123 100644
--- a/engines/queen/talk.cpp
+++ b/engines/queen/talk.cpp
@@ -777,7 +777,7 @@ void Talk::defaultAnimation(
}
// Make sure that Person closes their mouth
- if (!isJoe && parameters->ff > 0)
+ if (!isJoe && parameters && parameters->ff > 0)
_vm->bankMan()->overpack(parameters->ff, startFrame, bankNum);
}
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index 8bc8025032..5111f1ae07 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -42,18 +42,42 @@
namespace Saga {
ActorData::ActorData() {
- memset(this, 0, sizeof(*this));
-}
+ _frames = NULL;
+ _frameListResourceId = 0;
+ _speechColor = 0;
+ _inScene = false;
+
+ _actorFlags = 0;
+ _currentAction = 0;
+ _facingDirection = 0;
+ _actionDirection = 0;
+ _actionCycle = 0;
+ _targetObject = 0;
+ _lastZone = NULL;
+
+ _cycleFrameSequence = 0;
+ _cycleDelay = 0;
+ _cycleTimeCount = 0;
+ _cycleFlags = 0;
+
+ _fallVelocity = 0;
+ _fallAcceleration = 0;
+ _fallPosition = 0;
-ActorData::~ActorData() {
- if (!_shareFrames)
- free(_frames);
- free(_tileDirections);
- free(_walkStepsPoints);
- freeSpriteList();
+ _dragonBaseFrame = 0;
+ _dragonStepCycle = 0;
+ _dragonMoveType = 0;
+
+ _frameNumber = 0;
+
+ _walkStepsCount = 0;
+ _walkStepIndex = 0;
+
+ _walkFrameSequence = 0;
}
+
void ActorData::saveState(Common::OutSaveFile *out) {
- int i = 0;
+ uint i = 0;
CommonObjectData::saveState(out);
out->writeUint16LE(_actorFlags);
out->writeSint32LE(_currentAction);
@@ -74,13 +98,13 @@ void ActorData::saveState(Common::OutSaveFile *out) {
out->writeByte(_dragonMoveType);
out->writeSint32LE(_frameNumber);
- out->writeSint32LE(_tileDirectionsAlloced);
- for (i = 0; i < _tileDirectionsAlloced; i++) {
+ out->writeSint32LE(_tileDirections.size());
+ for (i = 0; i < _tileDirections.size(); i++) {
out->writeByte(_tileDirections[i]);
}
- out->writeSint32LE(_walkStepsAlloced);
- for (i = 0; i < _walkStepsAlloced; i++) {
+ out->writeSint32LE(_walkStepsPoints.size());
+ for (i = 0; i < _walkStepsPoints.size(); i++) {
out->writeSint16LE(_walkStepsPoints[i].x);
out->writeSint16LE(_walkStepsPoints[i].y);
}
@@ -93,7 +117,7 @@ void ActorData::saveState(Common::OutSaveFile *out) {
}
void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
- int i = 0;
+ uint i = 0;
CommonObjectData::loadState(in);
_actorFlags = in->readUint16LE();
_currentAction = in->readSint32LE();
@@ -125,13 +149,13 @@ void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
_frameNumber = in->readSint32LE();
- setTileDirectionsSize(in->readSint32LE(), true);
- for (i = 0; i < _tileDirectionsAlloced; i++) {
+ _tileDirections.resize(in->readSint32LE());
+ for (i = 0; i < _tileDirections.size(); i++) {
_tileDirections[i] = in->readByte();
}
- setWalkStepsPointsSize(in->readSint32LE(), true);
- for (i = 0; i < _walkStepsAlloced; i++) {
+ _walkStepsPoints.resize(in->readSint32LE());
+ for (i = 0; i < _walkStepsPoints.size(); i++) {
_walkStepsPoints[i].x = in->readSint16LE();
_walkStepsPoints[i].y = in->readSint16LE();
}
@@ -143,38 +167,16 @@ void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
_walkFrameSequence = in->readSint32LE();
}
-void ActorData::setTileDirectionsSize(int size, bool forceRealloc) {
- if ((size <= _tileDirectionsAlloced) && !forceRealloc) {
- return;
- }
- _tileDirectionsAlloced = size;
- _tileDirections = (byte*)realloc(_tileDirections, _tileDirectionsAlloced * sizeof(*_tileDirections));
-}
-
void ActorData::cycleWrap(int cycleLimit) {
if (_actionCycle >= cycleLimit)
_actionCycle = 0;
}
-void ActorData::setWalkStepsPointsSize(int size, bool forceRealloc) {
- if ((size <= _walkStepsAlloced) && !forceRealloc) {
- return;
- }
- _walkStepsAlloced = size;
- _walkStepsPoints = (Point*)realloc(_walkStepsPoints, _walkStepsAlloced * sizeof(*_walkStepsPoints));
-}
-
void ActorData::addWalkStepPoint(const Point &point) {
- setWalkStepsPointsSize(_walkStepsCount + 1, false);
+ _walkStepsPoints.resize(_walkStepsCount + 1);
_walkStepsPoints[_walkStepsCount++] = point;
}
-void ActorData::freeSpriteList() {
- _spriteList.freeMem();
-}
-
-
-
static int commonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
int p1 = obj1->_location.y - obj1->_location.z;
int p2 = obj2->_location.y - obj2->_location.z;
@@ -212,26 +214,14 @@ static int tileCommonObjectCompare(const CommonObjectDataPointer& obj1, const Co
Actor::Actor(SagaEngine *vm) : _vm(vm) {
int i;
- byte *stringsPointer;
- size_t stringsLength;
- ActorData *actor;
- ObjectData *obj;
+ ByteArray stringsData;
debug(9, "Actor::Actor()");
_handleActionDiv = 15;
- _actors = NULL;
- _actorsCount = 0;
-
- _objs = NULL;
- _objsCount = 0;
-
#ifdef ACTOR_DEBUG
_debugPointsCount = 0;
#endif
- _protagStates = NULL;
- _protagStatesCount = 0;
-
_pathList.resize(600);
_pathListIndex = 0;
@@ -242,7 +232,7 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
_yCellCount = _vm->_scene->getHeight();
_xCellCount = _vm->getDisplayInfo().width;
- _pathCell = (int8 *)malloc(_yCellCount * _xCellCount * sizeof(*_pathCell));
+ _pathCell.resize(_yCellCount * _xCellCount);
_pathRect.left = 0;
_pathRect.right = _vm->getDisplayInfo().width;
@@ -260,19 +250,17 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
if (_vm->getGameId() == GID_ITE) {
- _vm->_resource->loadResource(_actorContext, _vm->getResourceDescription()->actorsStringsResourceId, stringsPointer, stringsLength);
+ _vm->_resource->loadResource(_actorContext, _vm->getResourceDescription()->actorsStringsResourceId, stringsData);
- _vm->loadStrings(_actorsStrings, stringsPointer, stringsLength);
- free(stringsPointer);
+ _vm->loadStrings(_actorsStrings, stringsData);
}
if (_vm->getGameId() == GID_ITE) {
- _actorsCount = ITE_ACTORCOUNT;
- _actors = (ActorData **)malloc(_actorsCount * sizeof(*_actors));
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i] = new ActorData();
- actor->_id = actorIndexToId(i);
+ _actors.resize(ITE_ACTORCOUNT);
+ i = 0;
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor, i++) {
actor->_index = i;
+ actor->_id = actorIndexToId(actor->_index);
debug(9, "init actor id=%d index=%d", actor->_id, actor->_index);
actor->_nameIndex = ITE_ActorTable[i].nameIndex;
actor->_scriptEntrypointNumber = ITE_ActorTable[i].scriptEntrypointNumber;
@@ -294,12 +282,11 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
warning("Disabling actor Id=%d index=%d", actor->_id, actor->_index);
}
}
- _objsCount = ITE_OBJECTCOUNT;
- _objs = (ObjectData **)malloc(_objsCount * sizeof(*_objs));
- for (i = 0; i < _objsCount; i++) {
- obj = _objs[i] = new ObjectData();
- obj->_id = objIndexToId(i);
+ _objs.resize(ITE_OBJECTCOUNT);
+ i = 0;
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj, i++) {
obj->_index = i;
+ obj->_id = objIndexToId(obj->_index);
debug(9, "init obj id=%d index=%d", obj->_id, obj->_index);
obj->_nameIndex = ITE_ObjectTable[i].nameIndex;
obj->_scriptEntrypointNumber = ITE_ObjectTable[i].scriptEntrypointNumber;
@@ -318,69 +305,42 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
Actor::~Actor() {
debug(9, "Actor::~Actor()");
-
- free(_pathCell);
- _actorsStrings.freeMem();
- //release resources
- freeProtagStates();
- freeActorList();
- freeObjList();
-}
-
-void Actor::freeProtagStates() {
- int i;
- for (i = 0; i < _protagStatesCount; i++) {
- free(_protagStates[i]._frames);
- }
- free(_protagStates);
- _protagStates = NULL;
- _protagStatesCount = 0;
}
-void Actor::loadFrameList(int frameListResourceId, ActorFrameSequence *&framesPointer, int &framesCount) {
- byte *resourcePointer;
- size_t resourceLength;
+void Actor::loadFrameList(int frameListResourceId, ActorFrameSequences &frames) {
+ ByteArray resourceData;
debug(9, "Loading frame resource id %d", frameListResourceId);
- _vm->_resource->loadResource(_actorContext, frameListResourceId, resourcePointer, resourceLength);
-
- framesCount = resourceLength / 16;
- debug(9, "Frame resource contains %d frames (res length is %d)", framesCount, (int)resourceLength);
+ _vm->_resource->loadResource(_actorContext, frameListResourceId, resourceData);
- framesPointer = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * framesCount);
- if (framesPointer == NULL && framesCount != 0) {
- memoryError("Actor::loadFrameList");
- }
+ frames.resize(resourceData.size() / 16);
+ debug(9, "Frame resource contains %d frames (res length is %d)", frames.size(), (int)resourceData.size());
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _actorContext->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _actorContext->isBigEndian());
- for (int i = 0; i < framesCount; i++) {
- debug(9, "frameType %d", i);
+ for (ActorFrameSequences::iterator frame = frames.begin(); frame != frames.end(); ++frame) {
for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
// Load all four orientations
- framesPointer[i].directions[orient].frameIndex = readS.readUint16();
+ frame->directions[orient].frameIndex = readS.readUint16();
if (_vm->getGameId() == GID_ITE) {
- framesPointer[i].directions[orient].frameCount = readS.readSint16();
+ frame->directions[orient].frameCount = readS.readSint16();
} else {
- framesPointer[i].directions[orient].frameCount = readS.readByte();
+ frame->directions[orient].frameCount = readS.readByte();
readS.readByte();
}
- if (framesPointer[i].directions[orient].frameCount < 0)
- warning("frameCount < 0 (%d)", framesPointer[i].directions[orient].frameCount);
- debug(9, "frameIndex %d frameCount %d", framesPointer[i].directions[orient].frameIndex, framesPointer[i].directions[orient].frameCount);
+ if (frame->directions[orient].frameCount < 0)
+ warning("frameCount < 0 (%d)", frame->directions[orient].frameCount);
+ debug(9, "frameIndex %d frameCount %d", frame->directions[orient].frameIndex, frame->directions[orient].frameCount);
}
}
-
- free(resourcePointer);
}
bool Actor::loadActorResources(ActorData *actor) {
bool gotSomething = false;
if (actor->_frameListResourceId) {
- loadFrameList(actor->_frameListResourceId, actor->_frames, actor->_framesCount);
-
- actor->_shareFrames = false;
+ loadFrameList(actor->_frameListResourceId, actor->_framesContainer);
+ actor->_frames = &actor->_framesContainer;
gotSomething = true;
} else {
@@ -400,26 +360,18 @@ bool Actor::loadActorResources(ActorData *actor) {
return gotSomething;
}
-void Actor::freeActorList() {
- int i;
- ActorData *actor;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
- delete actor;
- }
- free(_actors);
- _actors = NULL;
- _actorsCount = 0;
-}
-
void Actor::loadActorSpriteList(ActorData *actor) {
- int lastFrame = 0;
+ uint lastFrame = 0;
+ uint curFrameIndex;
int resourceId = actor->_spriteListResourceId;
-
- for (int i = 0; i < actor->_framesCount; i++) {
- for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
- if (actor->_frames[i].directions[orient].frameIndex > lastFrame) {
- lastFrame = actor->_frames[i].directions[orient].frameIndex;
+
+ if (actor->_frames != NULL) {
+ for (ActorFrameSequences::const_iterator i = actor->_frames->begin(); i != actor->_frames->end(); ++i) {
+ for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
+ curFrameIndex = i->directions[orient].frameIndex;
+ if (curFrameIndex > lastFrame) {
+ lastFrame = curFrameIndex;
+ }
}
}
}
@@ -430,7 +382,7 @@ void Actor::loadActorSpriteList(ActorData *actor) {
if (_vm->getGameId() == GID_ITE) {
if (actor->_flags & kExtended) {
- while ((lastFrame >= actor->_spriteList.spriteCount)) {
+ while ((lastFrame >= actor->_spriteList.size())) {
resourceId++;
debug(9, "Appending to actor sprite list %d", resourceId);
_vm->_sprite->loadList(resourceId, actor->_spriteList);
@@ -441,9 +393,7 @@ void Actor::loadActorSpriteList(ActorData *actor) {
void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResourceID, int protagStatesCount, int protagStatesResourceID) {
int i, j;
- ActorData *actor;
- byte* actorListData;
- size_t actorListLength;
+ ByteArray actorListData;
byte walk[128];
byte acv[6];
int movementSpeed;
@@ -451,24 +401,20 @@ void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResource
int walkStepCount;
int stateResourceId;
- freeActorList();
-
- _vm->_resource->loadResource(_actorContext, actorsResourceID, actorListData, actorListLength);
+ _vm->_resource->loadResource(_actorContext, actorsResourceID, actorListData);
- _actorsCount = actorCount;
-
- if (actorListLength != (uint)_actorsCount * ACTOR_INHM_SIZE) {
+ if (actorListData.size() != (uint)actorCount * ACTOR_INHM_SIZE) {
error("Actor::loadActorList wrong actorlist length");
}
- MemoryReadStream actorS(actorListData, actorListLength);
+ ByteArrayReadStreamEndian actorS(actorListData);
- _actors = (ActorData **)malloc(_actorsCount * sizeof(*_actors));
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i] = new ActorData();
- actor->_id = objectIndexToId(kGameObjectActor, i); //actorIndexToId(i);
+ _actors.resize(actorCount);
+ i = 0;
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor, i++) {
actor->_index = i;
- debug(4, "init actor id=0x%x index=%d", actor->_id, actor->_index);
+ actor->_id = objectIndexToId(kGameObjectActor, actor->_index); //actorIndexToId(i);
+ debug(4, "init actor id=0x%X index=%d", actor->_id, actor->_index);
actorS.readUint32LE(); //next displayed
actorS.readByte(); //type
actor->_flags = actorS.readByte();
@@ -531,86 +477,58 @@ void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResource
}
// actorS.seek(6, SEEK_CUR); //action vars
}
- free(actorListData);
- _actors[protagonistIdx]->_flags |= kProtagonist | kExtended;
+ _actors[protagonistIdx]._flags |= kProtagonist | kExtended;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
//if (actor->_flags & kProtagonist) {
loadActorResources(actor);
//break;
//}
}
- _centerActor = _protagonist = _actors[protagonistIdx];
+ _centerActor = _protagonist = &_actors[protagonistIdx];
_protagState = 0;
if (protagStatesResourceID) {
- if (!_protagonist->_shareFrames)
- free(_protagonist->_frames);
- freeProtagStates();
-
- _protagStates = (ProtagStateData *)malloc(sizeof(ProtagStateData) * protagStatesCount);
+ _protagStates.resize(protagStatesCount);
- byte *idsResourcePointer;
- size_t idsResourceLength;
+ ByteArray idsResourceData;
- _vm->_resource->loadResource(_actorContext, protagStatesResourceID,
- idsResourcePointer, idsResourceLength);
+ _vm->_resource->loadResource(_actorContext, protagStatesResourceID, idsResourceData);
- if (idsResourceLength < (size_t)protagStatesCount * 4) {
+ if (idsResourceData.size() < (size_t)protagStatesCount * 4) {
error("Wrong protagonist states resource");
}
- MemoryReadStream statesIds(idsResourcePointer, idsResourceLength);
+ ByteArrayReadStreamEndian statesIds(idsResourceData);
for (i = 0; i < protagStatesCount; i++) {
stateResourceId = statesIds.readUint32LE();
- loadFrameList(stateResourceId, _protagStates[i]._frames, _protagStates[i]._framesCount);
+ loadFrameList(stateResourceId, _protagStates[i]._frames);
}
- free(idsResourcePointer);
- _protagonist->_frames = _protagStates[_protagState]._frames;
- _protagonist->_framesCount = _protagStates[_protagState]._framesCount;
- _protagonist->_shareFrames = true;
+ _protagonist->_frames = &_protagStates[_protagState]._frames;
}
- _protagStatesCount = protagStatesCount;
-}
-
-void Actor::freeObjList() {
- int i;
- ObjectData *object;
- for (i = 0; i < _objsCount; i++) {
- object = _objs[i];
- delete object;
- }
- free(_objs);
- _objs = NULL;
- _objsCount = 0;
}
void Actor::loadObjList(int objectCount, int objectsResourceID) {
- int i;
+ uint i;
int frameListResourceId;
- ObjectData *object;
- byte* objectListData;
- size_t objectListLength;
- freeObjList();
+ ByteArray objectListData;
- _vm->_resource->loadResource(_actorContext, objectsResourceID, objectListData, objectListLength);
+ _vm->_resource->loadResource(_actorContext, objectsResourceID, objectListData);
- _objsCount = objectCount;
+ _objs.resize(objectCount);
- MemoryReadStream objectS(objectListData, objectListLength);
+ ByteArrayReadStreamEndian objectS(objectListData);
- _objs = (ObjectData **)malloc(_objsCount * sizeof(*_objs));
- for (i = 0; i < _objsCount; i++) {
- object = _objs[i] = new ObjectData();
- object->_id = objectIndexToId(kGameObjectObject, i);
+ i = 0;
+ for (ObjectDataArray::iterator object = _objs.begin(); object != _objs.end(); ++object, i++) {
object->_index = i;
+ object->_id = objectIndexToId(kGameObjectObject, object->_index);
debug(9, "init object id=%d index=%d", object->_id, object->_index);
objectS.readUint32LE(); //next displayed
objectS.readByte(); //type
@@ -635,7 +553,6 @@ void Actor::loadObjList(int objectCount, int objectsResourceID) {
objectS.readUint16LE(); //BOTTOM
object->_interactBits = objectS.readUint16LE();
}
- free(objectListData);
}
void Actor::takeExit(uint16 actorId, const HitZone *hitZone) {
@@ -683,7 +600,7 @@ void Actor::stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit,
event.param5 = ID_NOTHING; // With Object
event.param6 = ID_PROTAG; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
}
@@ -693,7 +610,7 @@ ObjectData *Actor::getObj(uint16 objId) {
if (!validObjId(objId))
error("Actor::getObj Wrong objId 0x%X", objId);
- obj = _objs[objIdToIndex(objId)];
+ obj = &_objs[objIdToIndex(objId)];
if (obj->_disabled)
error("Actor::getObj disabled objId 0x%X", objId);
@@ -716,7 +633,7 @@ ActorData *Actor::getActor(uint16 actorId) {
return _protagonist;
}
- actor = _actors[actorIdToIndex(actorId)];
+ actor = &_actors[actorIdToIndex(actorId)];
if (actor->_disabled)
error("Actor::getActor disabled actorId 0x%X", actorId);
@@ -729,12 +646,8 @@ void Actor::setProtagState(int state) {
#ifdef ENABLE_IHNM
if (_vm->getGameId() == GID_IHNM) {
- if (!_protagonist->_shareFrames)
- free(_protagonist->_frames);
- _protagonist->_frames = _protagStates[state]._frames;
- _protagonist->_framesCount = _protagStates[state]._framesCount;
- _protagonist->_shareFrames = true;
+ _protagonist->_frames = &_protagStates[state]._frames;
}
#endif
@@ -797,15 +710,18 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
if ((actor->_facingDirection < kDirUp) || (actor->_facingDirection > kDirUpLeft))
error("Actor::getActorFrameRange Wrong direction 0x%X actorId 0x%X", actor->_facingDirection, actorId);
+ ActorFrameSequences *frames;
+ frames = actor->_frames;
+
if (_vm->getGameId() == GID_ITE) {
- if (frameType >= actor->_framesCount) {
- warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, actor->_framesCount, actorId);
+ if (uint(frameType) >= frames->size()) {
+ warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, frames->size(), actorId);
return &def;
}
fourDirection = actorDirectionsLUT[actor->_facingDirection];
- return &actor->_frames[frameType].directions[fourDirection];
+ return &(*frames)[frameType].directions[fourDirection];
}
#ifdef ENABLE_IHNM
@@ -816,12 +732,12 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
// Both of them are invisible and immovable
// There is no point to keep throwing warnings about this, the original checks for
// a valid framecount too
- if (actor->_framesCount == 0) {
+ if ((frames == NULL) || (frames->empty())) {
return &def;
}
- frameType = CLIP(frameType, 0, actor->_framesCount - 1);
+ frameType = CLIP(frameType, 0, int(frames->size() - 1));
fourDirection = actorDirectionsLUT[actor->_facingDirection];
- return &actor->_frames[frameType].directions[fourDirection];
+ return &(*frames)[frameType].directions[fourDirection];
}
#endif
@@ -1085,9 +1001,6 @@ void Actor::drawOrderListAdd(const CommonObjectDataPointer& element, CompareFunc
}
void Actor::createDrawOrderList() {
- int i;
- ActorData *actor;
- ObjectData *obj;
CompareFunction compareFunction = 0;
if (_vm->_scene->getFlags() & kSceneFlagISO) {
@@ -1102,8 +1015,7 @@ void Actor::createDrawOrderList() {
}
_drawOrderList.clear();
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
if (!actor->_inScene)
continue;
@@ -1113,8 +1025,7 @@ void Actor::createDrawOrderList() {
}
}
- for (i = 0; i < _objsCount; i++) {
- obj = _objs[i];
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
if (obj->_disabled)
continue;
@@ -1146,19 +1057,20 @@ bool Actor::getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber
ActorData *actor = (ActorData *)commonObjectData;
spriteList = &(actor->_spriteList);
frameNumber = actor->_frameNumber;
- if (spriteList->infoList == NULL)
+ if (spriteList->empty()) {
loadActorSpriteList(actor);
+ }
} else if (validObjId(commonObjectData->_id)) {
spriteList = &_vm->_sprite->_mainSprites;
frameNumber = commonObjectData->_spriteListResourceId;
}
- if (spriteList->spriteCount == 0) {
+ if (spriteList->empty()) {
return false;
}
- if ((frameNumber < 0) || (spriteList->spriteCount <= frameNumber)) {
+ if ((frameNumber < 0) || (spriteList->size() <= uint(frameNumber))) {
debug(1, "Actor::getSpriteParams frameNumber invalid for %s id 0x%X (%d)",
validObjId(commonObjectData->_id) ? "object" : "actor",
commonObjectData->_id, frameNumber);
@@ -1184,7 +1096,7 @@ void Actor::drawActors() {
return;
}
- if (_vm->_scene->_entryList.entryListCount == 0) {
+ if (_vm->_scene->_entryList.empty()) {
return;
}
@@ -1222,12 +1134,13 @@ void Actor::drawSpeech() {
ActorData *actor;
int width, height;
int stringLength = strlen(_activeSpeech.strings[0]);
- char *outputString = (char*)calloc(stringLength + 1, 1);
+ Common::Array<char> outputString;
+ outputString.resize(stringLength + 1);
if (_activeSpeech.speechFlags & kSpeakSlow)
- strncpy(outputString, _activeSpeech.strings[0], _activeSpeech.slowModeCharIndex + 1);
+ strncpy(&outputString.front(), _activeSpeech.strings[0], _activeSpeech.slowModeCharIndex + 1);
else
- strncpy(outputString, _activeSpeech.strings[0], stringLength);
+ strncpy(&outputString.front(), _activeSpeech.strings[0], stringLength);
if (_activeSpeech.actorsCount > 1) {
height = _vm->_font->getHeight(kKnownFontScript);
@@ -1244,15 +1157,13 @@ void Actor::drawSpeech() {
else if (_vm->getGameId() == GID_IHNM)
textPoint.y = 10; // CLIP(actor->_screenPosition.y - 160, 10, _vm->_scene->getHeight(true) - 10 - height);
- _vm->_font->textDraw(kKnownFontScript, outputString, textPoint,
+ _vm->_font->textDraw(kKnownFontScript, &outputString.front(), textPoint,
_activeSpeech.speechColor[i], _activeSpeech.outlineColor[i], _activeSpeech.getFontFlags(i));
}
} else {
- _vm->_font->textDrawRect(kKnownFontScript, outputString, _activeSpeech.drawRect, _activeSpeech.speechColor[0],
+ _vm->_font->textDrawRect(kKnownFontScript, &outputString.front(), _activeSpeech.drawRect, _activeSpeech.speechColor[0],
_activeSpeech.outlineColor[0], _activeSpeech.getFontFlags(0));
}
-
- free(outputString);
}
void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
@@ -1302,9 +1213,9 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
// Check Script::sfDropObject for the other part of this hack
if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 3 &&
_vm->_scene->currentSceneNumber() == 59 && _activeSpeech.sampleResourceId == 286) {
- for (i = 0; i < _objsCount; i++) {
- if (_objs[i]->_id == 16385) { // the compact disk
- _objs[i]->_sceneNumber = 59;
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
+ if (obj->_id == 16385) { // the compact disk
+ obj->_sceneNumber = 59;
break;
}
}
@@ -1376,36 +1287,31 @@ void Actor::abortSpeech() {
}
void Actor::saveState(Common::OutSaveFile *out) {
- uint16 i;
out->writeSint16LE(getProtagState());
- for (i = 0; i < _actorsCount; i++) {
- ActorData *a = _actors[i];
- a->saveState(out);
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
+ actor->saveState(out);
}
- for (i = 0; i < _objsCount; i++) {
- ObjectData *o = _objs[i];
- o->saveState(out);
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
+ obj->saveState(out);
}
}
void Actor::loadState(Common::InSaveFile *in) {
- int32 i;
int16 protagState = in->readSint16LE();
- if (protagState != 0 || _protagonist->_shareFrames)
+ if (protagState != 0 || (_protagonist->shareFrames())) {
setProtagState(protagState);
+ }
- for (i = 0; i < _actorsCount; i++) {
- ActorData *a = _actors[i];
- a->loadState(_vm->getCurrentLoadVersion(), in);
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
+ actor->loadState(_vm->getCurrentLoadVersion(), in);
}
- for (i = 0; i < _objsCount; i++) {
- ObjectData *o = _objs[i];
- o->loadState(in);
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
+ obj->loadState(in);
}
}
diff --git a/engines/saga/actor.h b/engines/saga/actor.h
index 2f8fdea8ec..6f74eea771 100644
--- a/engines/saga/actor.h
+++ b/engines/saga/actor.h
@@ -196,6 +196,8 @@ struct ActorFrameSequence {
ActorFrameRange directions[ACTOR_DIRECTIONS_COUNT];
};
+typedef Common::Array<ActorFrameSequence> ActorFrameSequences;
+
uint pathLine(PointList &pointList, uint idx, const Point &point1, const Point &point2);
struct Location {
@@ -265,7 +267,7 @@ struct Location {
screenPoint.x = x / ACTOR_LMULT;
screenPoint.y = y / ACTOR_LMULT - z;
}
- void fromStream(MemoryReadStream &stream) {
+ void fromStream(Common::ReadStream &stream) {
x = stream.readUint16LE();
y = stream.readUint16LE();
z = stream.readUint16LE();
@@ -323,6 +325,21 @@ public:
_screenDepth = in->readSint32LE();
_screenScale = in->readSint32LE();
}
+
+ CommonObjectData() {
+ _disabled = false;
+ _index = 0;
+ _id = 0;
+ _scriptEntrypointNumber = 0;
+
+ _flags = 0;
+ _nameIndex = 0;
+ _sceneNumber = 0;
+ _spriteListResourceId = 0;
+
+ _screenDepth = 0;
+ _screenScale = 0;
+ }
};
typedef CommonObjectData *CommonObjectDataPointer;
@@ -333,19 +350,21 @@ class ObjectData: public CommonObjectData {
public:
//constant
uint16 _interactBits;
+
ObjectData() {
- memset(this, 0, sizeof(*this));
+ _interactBits = 0;
}
};
+typedef Common::Array<ObjectData> ObjectDataArray;
+
class ActorData: public CommonObjectData {
public:
//constant
SpriteList _spriteList; // sprite list data
- bool _shareFrames;
- ActorFrameSequence *_frames; // Actor's frames
- int _framesCount; // Actor's frames count
+ ActorFrameSequences *_frames; // Actor's frames
+ ActorFrameSequences _framesContainer; // Actor's frames
int _frameListResourceId; // Actor's frame list resource id
byte _speechColor; // Actor dialogue color
@@ -376,11 +395,9 @@ public:
int32 _frameNumber; // current frame number
- int32 _tileDirectionsAlloced;
- byte *_tileDirections;
+ ByteArray _tileDirections;
- int32 _walkStepsAlloced;
- Point *_walkStepsPoints;
+ Common::Array<Point> _walkStepsPoints;
int32 _walkStepsCount;
int32 _walkStepIndex;
@@ -391,21 +408,21 @@ public:
public:
ActorData();
- ~ActorData();
void saveState(Common::OutSaveFile *out);
void loadState(uint32 version, Common::InSaveFile *in);
- void setTileDirectionsSize(int size, bool forceRealloc);
void cycleWrap(int cycleLimit);
- void setWalkStepsPointsSize(int size, bool forceRealloc);
void addWalkStepPoint(const Point &point);
- void freeSpriteList();
+ bool shareFrames() {
+ return ((_frames != NULL) && (_frames != &_framesContainer));
+ }
};
+typedef Common::Array<ActorData> ActorDataArray;
+
struct ProtagStateData {
- ActorFrameSequence *_frames; // Actor's frames
- int _framesCount; // Actor's frames count
+ ActorFrameSequences _frames; // Actor's frames
};
@@ -450,15 +467,17 @@ public:
void cmdActorWalkTo(int argc, const char **argv);
- bool validActorId(uint16 id) { return (id == ID_PROTAG) || ((id >= objectIndexToId(kGameObjectActor, 0)) && (id < objectIndexToId(kGameObjectActor, _actorsCount))); }
+ bool validActorId(uint16 id) {
+ return (id == ID_PROTAG) || ((id >= objectIndexToId(kGameObjectActor, 0)) && (id < objectIndexToId(kGameObjectActor, _actors.size())));
+ }
int actorIdToIndex(uint16 id) { return (id == ID_PROTAG) ? 0 : objectIdToIndex(id); }
uint16 actorIndexToId(int index) { return (index == 0) ? ID_PROTAG : objectIndexToId(kGameObjectActor, index); }
ActorData *getActor(uint16 actorId);
- ActorData *getFirstActor() { return _actors[0]; }
+ ActorData *getFirstActor() { return &_actors.front(); }
// clarification: Obj - means game object, such Hat, Spoon etc, Object - means Actor,Obj,HitZone,StepZone
- bool validObjId(uint16 id) { return (id >= objectIndexToId(kGameObjectObject, 0)) && (id < objectIndexToId(kGameObjectObject, _objsCount)); }
+ bool validObjId(uint16 id) { return (id >= objectIndexToId(kGameObjectObject, 0)) && (id < objectIndexToId(kGameObjectObject, _objs.size())); }
int objIdToIndex(uint16 id) { return objectIdToIndex(id); }
uint16 objIndexToId(int index) { return objectIndexToId(kGameObjectObject, index); }
ObjectData *getObj(uint16 objId);
@@ -525,18 +544,14 @@ public:
void setProtagState(int state);
int getProtagState() { return _protagState; }
- void freeProtagStates();
-
- void freeActorList();
void loadActorList(int protagonistIdx, int actorCount, int actorsResourceID,
int protagStatesCount, int protagStatesResourceID);
- void freeObjList();
void loadObjList(int objectCount, int objectsResourceID);
protected:
friend class Script;
bool loadActorResources(ActorData *actor);
- void loadFrameList(int frameListResourceId, ActorFrameSequence *&framesPointer, int &framesCount);
+ void loadFrameList(int frameListResourceId, ActorFrameSequences &frames);
private:
void stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped);
void loadActorSpriteList(ActorData *actor);
@@ -584,11 +599,9 @@ private:
protected:
//constants
- int _actorsCount;
- ActorData **_actors;
+ ActorDataArray _actors;
- int _objsCount;
- ObjectData **_objs;
+ ObjectDataArray _objs;
SagaEngine *_vm;
ResourceContext *_actorContext;
@@ -613,8 +626,7 @@ protected:
bool _dragonHunt;
private:
- ProtagStateData *_protagStates;
- int _protagStatesCount;
+ Common::Array<ProtagStateData> _protagStates;
//path stuff
struct PathNode {
@@ -629,7 +641,7 @@ private:
Rect _barrierList[ACTOR_BARRIERS_MAX];
int _barrierCount;
- int8 *_pathCell;
+ Common::Array<int8> _pathCell;
int _xCellCount;
int _yCellCount;
diff --git a/engines/saga/actor_walk.cpp b/engines/saga/actor_walk.cpp
index 21643ac1de..5a8ea0c856 100644
--- a/engines/saga/actor_walk.cpp
+++ b/engines/saga/actor_walk.cpp
@@ -179,9 +179,8 @@ void Actor::actorFaceTowardsPoint(uint16 actorId, const Location &toLocation) {
}
void Actor::updateActorsScene(int actorsEntrance) {
- int i, j;
+ int j;
int followerDirection;
- ActorData *actor;
Location tempLocation;
Location possibleLocation;
Point delta;
@@ -196,14 +195,13 @@ void Actor::updateActorsScene(int actorsEntrance) {
_activeSpeech.playing = false;
_protagonist = NULL;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
actor->_inScene = false;
- actor->_spriteList.freeMem();
+ actor->_spriteList.clear();
if (actor->_disabled) {
continue;
}
- if ((actor->_flags & (kProtagonist | kFollower)) || (i == 0)) {
+ if ((actor->_flags & (kProtagonist | kFollower)) || (actor->_index == 0)) {
if (actor->_flags & kProtagonist) {
actor->_finalTarget = actor->_location;
_centerActor = _protagonist = actor;
@@ -227,12 +225,12 @@ void Actor::updateActorsScene(int actorsEntrance) {
if (_protagonist == NULL)
return;
- if ((actorsEntrance >= 0) && (_vm->_scene->_entryList.entryListCount > 0)) {
- if (_vm->_scene->_entryList.entryListCount <= actorsEntrance) {
+ if ((actorsEntrance >= 0) && (!_vm->_scene->_entryList.empty())) {
+ if (_vm->_scene->_entryList.size() <= uint(actorsEntrance)) {
actorsEntrance = 0; //OCEAN bug
}
- sceneEntry = _vm->_scene->_entryList.getEntry(actorsEntrance);
+ sceneEntry = &_vm->_scene->_entryList[actorsEntrance];
if (_vm->_scene->getFlags() & kSceneFlagISO) {
_protagonist->_location = sceneEntry->location;
} else {
@@ -266,8 +264,7 @@ void Actor::updateActorsScene(int actorsEntrance) {
followerDirection = _protagonist->_facingDirection + 3;
calcScreenPosition(_protagonist);
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
if (actor->_flags & (kFollower)) {
actor->_facingDirection = actor->_actionDirection = _protagonist->_facingDirection;
actor->_currentAction = kActionWait;
@@ -323,8 +320,6 @@ void Actor::updateActorsScene(int actorsEntrance) {
}
void Actor::handleActions(int msec, bool setup) {
- int i;
- ActorData *actor;
ActorFrameRange *frameRange;
int state;
int speed;
@@ -336,12 +331,11 @@ void Actor::handleActions(int msec, bool setup) {
Point hitPoint;
Location pickLocation;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
if (!actor->_inScene)
continue;
- if ((_vm->getGameId() == GID_ITE) && (i == ACTOR_DRAGON_INDEX)) {
+ if ((_vm->getGameId() == GID_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) {
moveDragon(actor);
continue;
}
@@ -722,7 +716,7 @@ void Actor::handleActions(int msec, bool setup) {
void Actor::direct(int msec) {
- if (_vm->_scene->_entryList.entryListCount == 0) {
+ if (_vm->_scene->_entryList.empty()) {
return;
}
@@ -866,8 +860,6 @@ bool Actor::followProtagonist(ActorData *actor) {
bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
ActorData *actor;
- ActorData *anotherActor;
- int i;
Rect testBox;
Rect testBox2;
@@ -943,7 +935,7 @@ bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
int max = _vm->getGameId() == GID_ITE ? 8 : 4;
- for (i = 1; i < max; i++) {
+ for (int i = 1; i < max; i++) {
pointAdd = pointFrom;
pointAdd.y += i;
if (_vm->_scene->canWalk(pointAdd)) {
@@ -978,9 +970,7 @@ bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
collision.x = ACTOR_COLLISION_WIDTH * actor->_screenScale / (256 * 2);
collision.y = ACTOR_COLLISION_HEIGHT * actor->_screenScale / (256 * 2);
-
- for (i = 0; (i < _actorsCount) && (_barrierCount < ACTOR_BARRIERS_MAX); i++) {
- anotherActor = _actors[i];
+ for (ActorDataArray::iterator anotherActor = _actors.begin(); (anotherActor != _actors.end()) && (_barrierCount < ACTOR_BARRIERS_MAX); ++anotherActor) {
if (!anotherActor->_inScene)
continue;
if (anotherActor == actor)
@@ -1067,8 +1057,8 @@ bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
return false;
} else {
if (actor->_flags & kProtagonist) {
- _actors[1]->_actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2
- _actors[2]->_actorFlags &= ~kActorNoFollow;
+ _actors[1]._actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2
+ _actors[2]._actorFlags &= ~kActorNoFollow;
}
actor->_currentAction = (actor->_walkStepsCount >= ACTOR_MAX_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint;
actor->_walkFrameSequence = getFrameType(kFrameWalk);
@@ -1153,7 +1143,7 @@ void Actor::moveDragon(ActorData *actor) {
event.param4 = -1; // Object
event.param5 = -1; // With Object
event.param6 = -1; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_dragonHunt = false;
}
diff --git a/engines/saga/animation.cpp b/engines/saga/animation.cpp
index 0d65d2f191..4aee111aa7 100644
--- a/engines/saga/animation.cpp
+++ b/engines/saga/animation.cpp
@@ -42,8 +42,6 @@ namespace Saga {
Anim::Anim(SagaEngine *vm) : _vm(vm) {
uint16 i;
- _cutawayList = NULL;
- _cutawayListLength = 0;
_cutawayActive = false;
for (i = 0; i < MAX_ANIMATIONS; i++)
@@ -55,21 +53,16 @@ Anim::Anim(SagaEngine *vm) : _vm(vm) {
Anim::~Anim() {
reset();
-#ifdef ENABLE_IHNM
- freeCutawayList();
-#endif
}
#ifdef ENABLE_IHNM
-void Anim::loadCutawayList(const byte *resourcePointer, size_t resourceLength) {
- free(_cutawayList);
- _cutawayListLength = resourceLength / 8;
- _cutawayList = (Cutaway *)malloc(_cutawayListLength * sizeof(Cutaway));
+void Anim::loadCutawayList(const ByteArray &resourceData) {
+ _cutawayList.resize(resourceData.size() / 8);
- MemoryReadStream cutawayS(resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian cutawayS(resourceData);
- for (int i = 0; i < _cutawayListLength; i++) {
+ for (uint i = 0; i < _cutawayList.size(); i++) {
_cutawayList[i].backgroundResourceId = cutawayS.readUint16LE();
_cutawayList[i].animResourceId = cutawayS.readUint16LE();
_cutawayList[i].cycles = cutawayS.readSint16LE();
@@ -77,20 +70,16 @@ void Anim::loadCutawayList(const byte *resourcePointer, size_t resourceLength) {
}
}
-void Anim::freeCutawayList() {
- free(_cutawayList);
- _cutawayList = NULL;
- _cutawayListLength = 0;
+void Anim::clearCutawayList() {
+ _cutawayList.clear();
}
int Anim::playCutaway(int cut, bool fade) {
debug(0, "playCutaway(%d, %d)", cut, fade);
Event event;
- Event *q_event = NULL;
+ EventColumns *eventColumns = NULL;
bool startImmediately = false;
- byte *resourceData;
- size_t resourceDataLength;
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
_cutAwayFade = fade;
@@ -111,7 +100,7 @@ int Anim::playCutaway(int cut, bool fade) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -120,7 +109,7 @@ int Anim::playCutaway(int cut, bool fade) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
// Prepare cutaway
@@ -148,7 +137,7 @@ int Anim::playCutaway(int cut, bool fade) {
event.time = 0;
event.duration = 0;
event.param = _cutawayList[cut].backgroundResourceId;
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
} else {
showCutawayBg(_cutawayList[cut].backgroundResourceId);
}
@@ -180,9 +169,10 @@ int Anim::playCutaway(int cut, bool fade) {
// for the second from the left monitor in Ellen's chapter etc
// Therefore, skip the animation bit if animResourceId is 0 and only show the background
if (_cutawayList[cut].animResourceId != 0) {
- _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData, resourceDataLength);
- load(MAX_ANIMATIONS + cutawaySlot, resourceData, resourceDataLength);
- free(resourceData);
+ ByteArray resourceData;
+ _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData);
+ load(MAX_ANIMATIONS + cutawaySlot, resourceData);
+
setCycles(MAX_ANIMATIONS + cutawaySlot, _cutawayList[cut].cycles);
setFrameTime(MAX_ANIMATIONS + cutawaySlot, 1000 / _cutawayList[cut].frameRate);
@@ -198,9 +188,9 @@ int Anim::playCutaway(int cut, bool fade) {
event.time = (40 / 3) * 1000 / _cutawayList[cut].frameRate;
if (fade)
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
else
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
return MAX_ANIMATIONS + cutawaySlot;
@@ -222,7 +212,7 @@ void Anim::returnFromCutaway() {
if (_cutawayActive) {
Event event;
- Event *q_event = NULL;
+ EventColumns *eventColumns = NULL;
if (_cutAwayFade) {
static PalEntry cur_pal[PAL_ENTRIES];
@@ -237,7 +227,7 @@ void Anim::returnFromCutaway() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -246,7 +236,7 @@ void Anim::returnFromCutaway() {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
// Clear the cutaway. Note that this sets _cutawayActive to false
@@ -257,9 +247,9 @@ void Anim::returnFromCutaway() {
event.duration = 0;
if (_cutAwayFade)
- q_event = _vm->_events->chain(q_event, &event); // chain with the other events
+ eventColumns = _vm->_events->chain(eventColumns, event); // chain with the other events
else
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
_vm->_scene->restoreScene();
@@ -279,7 +269,7 @@ void Anim::returnFromCutaway() {
event.op = kEventResumeAll;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event); // chain with the other events
+ _vm->_events->chain(eventColumns, event); // chain with the other events
// Draw the scene
event.type = kEvTImmediate;
@@ -287,7 +277,7 @@ void Anim::returnFromCutaway() {
event.op = kEventDraw;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event); // chain with the other events
+ _vm->_events->chain(eventColumns, event); // chain with the other events
// Handle fade up, if we previously faded down
if (_cutAwayFade) {
@@ -297,14 +287,14 @@ void Anim::returnFromCutaway() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = saved_pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypeWakeUp;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
}
@@ -338,22 +328,20 @@ void Anim::clearCutaway() {
void Anim::showCutawayBg(int bg) {
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- byte *resourceData;
- size_t resourceDataLength;
- byte *buf;
- size_t buflen;
+ ByteArray resourceData;
+ ByteArray image;
int width;
int height;
Event event;
static PalEntry pal[PAL_ENTRIES];
- _vm->_resource->loadResource(context, bg, resourceData, resourceDataLength);
- _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height);
+ _vm->_resource->loadResource(context, bg, resourceData);
+ _vm->decodeBGImage(resourceData, image, &width, &height);
- const byte *palPointer = _vm->getImagePal(resourceData, resourceDataLength);
+ const byte *palPointer = _vm->getImagePal(resourceData);
memcpy(pal, palPointer, sizeof(pal));
const Rect rect(width, height);
- _vm->_render->getBackGroundSurface()->blit(rect, buf);
+ _vm->_render->getBackGroundSurface()->blit(rect, image.getBuffer());
_vm->_render->setFullRefresh(true);
_vm->_frameCount++;
@@ -365,13 +353,10 @@ void Anim::showCutawayBg(int bg) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = pal;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
} else {
_vm->_gfx->setPalette(pal);
}
-
- free(buf);
- free(resourceData);
}
void Anim::startVideo(int vid, bool fade) {
@@ -397,18 +382,18 @@ void Anim::returnFromVideo() {
#endif
-void Anim::load(uint16 animId, const byte *animResourceData, size_t animResourceLength) {
+void Anim::load(uint16 animId, const ByteArray &resourceData) {
AnimationData *anim;
uint16 temp;
if (animId >= MAX_ANIMATIONS) {
if (animId >= MAX_ANIMATIONS + ARRAYSIZE(_cutawayAnimations))
error("Anim::load could not find unused animation slot");
- anim = _cutawayAnimations[animId - MAX_ANIMATIONS] = new AnimationData(animResourceData, animResourceLength);
+ anim = _cutawayAnimations[animId - MAX_ANIMATIONS] = new AnimationData();
} else
- anim = _animations[animId] = new AnimationData(animResourceData, animResourceLength);
+ anim = _animations[animId] = new AnimationData();
- MemoryReadStreamEndian headerReadS(anim->resourceData, anim->resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian headerReadS(resourceData, _vm->isBigEndian());
anim->magic = headerReadS.readUint16LE(); // cause ALWAYS LE
anim->screenWidth = headerReadS.readUint16();
anim->screenHeight = headerReadS.readUint16();
@@ -418,23 +403,30 @@ void Anim::load(uint16 animId, const byte *animResourceData, size_t animResource
anim->maxFrame = headerReadS.readByte() - 1;
anim->loopFrame = headerReadS.readByte() - 1;
temp = headerReadS.readUint16BE();
- anim->start = headerReadS.pos();
+ size_t start;
+
+ start = headerReadS.pos();
if (temp == (uint16)(-1)) {
temp = 0;
}
- anim->start += temp;
+ start += temp;
+ size_t dataOffset = headerReadS.pos();
+ if (dataOffset != start) {
+ warning("Anim::load animId=%d start != dataOffset 0x%X 0x%X", animId, uint(start), uint(dataOffset));
+ }
+
+ anim->resourceData.resize(resourceData.size() - dataOffset);
+
+ memcpy(anim->resourceData.getBuffer(), resourceData.getBuffer() + dataOffset, anim->resourceData.size());
// Cache frame offsets
// WORKAROUND: Cutaway with background resource ID 37 (loaded as cutaway #4) is ending credits.
// For some reason it has wrong number of frames specified in its header. So we calculate it here:
- if (animId > MAX_ANIMATIONS && _cutawayListLength > 4 && _cutawayList[4].backgroundResourceId == 37 && anim->maxFrame == 143)
+ if (animId > MAX_ANIMATIONS && _cutawayList.size() > 4 && _cutawayList[4].backgroundResourceId == 37 && anim->maxFrame == 143)
anim->maxFrame = fillFrameOffsets(anim, false);
- anim->frameOffsets = (size_t *)malloc((anim->maxFrame + 1) * sizeof(*anim->frameOffsets));
- if (anim->frameOffsets == NULL) {
- memoryError("Anim::load");
- }
+ anim->frameOffsets.resize(anim->maxFrame + 1);
fillFrameOffsets(anim);
@@ -504,7 +496,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.op = kEventFrame;
event.param = animId;
event.time = 10;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
// Nothing to render here (apart from the background, which is already rendered),
// so return
@@ -534,7 +526,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.op = kEventFrame;
event.param = animId;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
return;
}
@@ -575,7 +567,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = anim->frameTime + vectorTime;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
return;
} else {
@@ -601,7 +593,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.op = kEventFrame;
event.param = animId;
event.time = frameTime;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
void Anim::stop(uint16 animId) {
@@ -688,7 +680,7 @@ void Anim::decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_
error("decodeFrame() Buffer size inadequate");
}
- MemoryReadStream readS(anim->resourceData + frameOffset, anim->resourceLength - frameOffset);
+ Common::MemoryReadStream readS(&anim->resourceData[frameOffset], anim->resourceData.size() - frameOffset);
// FIXME: This is thrown when the first video of the IHNM end sequence is shown (the "turn off screen"
// video), however the video is played correctly and the rest of the end sequence continues normally
@@ -825,9 +817,7 @@ int Anim::fillFrameOffsets(AnimationData *anim, bool reallyFill) {
int i;
bool longData = isLongData();
- MemoryReadStreamEndian readS(anim->resourceData, anim->resourceLength, !_vm->isBigEndian()); // RLE has inversion BE<>LE
-
- readS.seek(12);
+ Common::MemoryReadStreamEndian readS(&anim->resourceData.front(), anim->resourceData.size(), !_vm->isBigEndian()); // RLE has inversion BE<>LE
while (readS.pos() != readS.size()) {
if (reallyFill) {
@@ -843,7 +833,7 @@ int Anim::fillFrameOffsets(AnimationData *anim, bool reallyFill) {
// including the frame header, is in big endian format
do {
markByte = readS.readByte();
-// debug(7, "_pos=%x currentFrame=%i markByte=%x", readS.pos(), currentFrame, markByte);
+// debug(7, "_pos=%X currentFrame=%i markByte=%X", readS.pos(), currentFrame, markByte);
switch (markByte) {
case SAGA_FRAME_START: // Start of frame
@@ -942,9 +932,9 @@ void Anim::animInfo() {
void Anim::cutawayInfo() {
uint16 i;
- _vm->_console->DebugPrintf("There are %d cutaways loaded:\n", _cutawayListLength);
+ _vm->_console->DebugPrintf("There are %d cutaways loaded:\n", _cutawayList.size());
- for (i = 0; i < _cutawayListLength; i++) {
+ for (i = 0; i < _cutawayList.size(); i++) {
_vm->_console->DebugPrintf("%02d: Bg res: %u Anim res: %u Cycles: %u Framerate: %u\n", i,
_cutawayList[i].backgroundResourceId, _cutawayList[i].animResourceId,
_cutawayList[i].cycles, _cutawayList[i].frameRate);
diff --git a/engines/saga/animation.h b/engines/saga/animation.h
index 72b145089c..c27909115e 100644
--- a/engines/saga/animation.h
+++ b/engines/saga/animation.h
@@ -66,8 +66,7 @@ struct Cutaway {
// Animation info array member
struct AnimationData {
- byte *resourceData;
- size_t resourceLength;
+ ByteArray resourceData;
uint16 magic;
@@ -80,10 +79,8 @@ struct AnimationData {
int16 maxFrame;
int16 loopFrame;
- int16 start;
-
int16 currentFrame;
- size_t *frameOffsets;
+ Common::Array<size_t> frameOffsets;
uint16 completed;
uint16 cycles;
@@ -93,17 +90,6 @@ struct AnimationData {
AnimationState state;
int16 linkId;
uint16 flags;
-
- AnimationData(const byte *animResourceData, size_t animResourceLength) {
- memset(this, 0, sizeof(*this));
- resourceLength = animResourceLength;
- resourceData = (byte*)malloc(animResourceLength);
- memcpy(resourceData, animResourceData, animResourceLength);
- }
- ~AnimationData() {
- free(frameOffsets);
- free(resourceData);
- }
};
class Anim {
@@ -111,8 +97,8 @@ public:
Anim(SagaEngine *vm);
~Anim();
- void loadCutawayList(const byte *resourcePointer, size_t resourceLength);
- void freeCutawayList();
+ void loadCutawayList(const ByteArray &resourceData);
+ void clearCutawayList();
int playCutaway(int cut, bool fade);
void endCutaway();
void returnFromCutaway();
@@ -123,7 +109,7 @@ public:
void endVideo();
void returnFromVideo();
- void load(uint16 animId, const byte *animResourceData, size_t animResourceLength);
+ void load(uint16 animId, const ByteArray &resourceData);
void freeId(uint16 animId);
void play(uint16 animId, int vectorTime, bool playing = true);
void link(int16 animId1, int16 animId2);
@@ -154,9 +140,9 @@ public:
bool hasCutaway() { return _cutawayActive; }
void setCutAwayMode(int mode) { _cutAwayMode = mode; }
- int cutawayListLength() { return _cutawayListLength; }
- int cutawayBgResourceID(int cutaway) { return _cutawayList[cutaway].backgroundResourceId; }
- int cutawayAnimResourceID(int cutaway) { return _cutawayList[cutaway].animResourceId; }
+// int cutawayListLength() { return _cutawayListLength; }
+// int cutawayBgResourceID(int cutaway) { return _cutawayList[cutaway].backgroundResourceId; }
+// int cutawayAnimResourceID(int cutaway) { return _cutawayList[cutaway].animResourceId; }
private:
void decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_t bufLength);
@@ -205,9 +191,8 @@ private:
SagaEngine *_vm;
AnimationData *_animations[MAX_ANIMATIONS];
AnimationData *_cutawayAnimations[2];
- Cutaway *_cutawayList;
+ Common::Array<Cutaway> _cutawayList;
PalEntry saved_pal[PAL_ENTRIES];
- int _cutawayListLength;
bool _cutawayActive;
int _cutAwayMode;
bool _cutAwayFade;
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 7913291527..e43f1ee5c7 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -56,9 +56,9 @@ struct SAGAGameDescription {
bool SagaEngine::isBigEndian() const { return isMacResources() && getGameId() == GID_ITE; }
bool SagaEngine::isMacResources() const { return (getPlatform() == Common::kPlatformMacintosh); }
-const GameResourceDescription *SagaEngine::getResourceDescription() { return _gameDescription->resourceDescription; }
+const GameResourceDescription *SagaEngine::getResourceDescription() const { return _gameDescription->resourceDescription; }
-const GameFontDescription *SagaEngine::getFontDescription(int index) {
+const GameFontDescription *SagaEngine::getFontDescription(int index) const {
assert(index < _gameDescription->fontsCount);
return &_gameDescription->fontDescriptions[index];
}
@@ -259,7 +259,7 @@ SaveStateDescriptor SagaMetaEngine::querySaveMetaInfos(const char *target, int s
version = SWAP_BYTES_32(version);
}
- debug(2, "Save version: %x", version);
+ debug(2, "Save version: 0x%X", version);
if (version < 4)
warning("This savegame is not endian-safe. There may be problems");
diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp
index 1f4091d07c..cf27ad7559 100644
--- a/engines/saga/events.cpp
+++ b/engines/saga/events.cpp
@@ -49,13 +49,12 @@ Events::Events(SagaEngine *vm) : _vm(vm) {
Events::~Events() {
debug(8, "Shutting down event subsystem...");
- freeList();
}
// Function to process event list once per frame.
// First advances event times, then processes each event with the appropriate
// handler depending on the type of event.
-int Events::handleEvents(long msec) {
+void Events::handleEvents(long msec) {
long delta_time;
int result;
@@ -64,7 +63,7 @@ int Events::handleEvents(long msec) {
// Process each event in list
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
- Event *event_p = &*eventi;
+ Event *event_p = &eventi->front();
// Call the appropriate event handler for the specific event type
switch (event_p->type) {
@@ -95,17 +94,15 @@ int Events::handleEvents(long msec) {
// handler
if ((result == kEvStDelete) || (result == kEvStInvalidCode)) {
// If there is no event chain, delete the base event.
- if (event_p->chain == NULL) {
+ if (eventi->size() < 2) {
eventi = _eventList.reverse_erase(eventi);
} else {
// If there is an event chain present, move the next event
// in the chain up, adjust it by the previous delta time,
// and reprocess the event
delta_time = event_p->time;
- Event *from_chain = event_p->chain;
- memcpy(event_p, from_chain, sizeof(*event_p));
- free(from_chain);
-
+ eventi->pop_front();
+ event_p = &eventi->front();
event_p->time += delta_time;
--eventi;
}
@@ -113,8 +110,6 @@ int Events::handleEvents(long msec) {
break;
}
}
-
- return SUCCESS;
}
int Events::handleContinuous(Event *event) {
@@ -177,9 +172,8 @@ int Events::handleContinuous(Event *event) {
// set flag of Dissolve to 1. It is a hack to simulate zero masking.
int w, h;
byte *maskBuffer;
- size_t len;
- _vm->_scene->getBGMaskInfo(w, h, maskBuffer, len);
+ _vm->_scene->getBGMaskInfo(w, h, maskBuffer);
rect.left = (_vm->getDisplayInfo().width - w) / 2;
rect.top = (_vm->getDisplayInfo().height - h) / 2;
rect.setWidth(w);
@@ -362,31 +356,26 @@ int Events::handleOneShot(Event *event) {
{
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- byte *resourceData;
- size_t resourceDataLength;
+ ByteArray resourceData;
- _vm->_resource->loadResource(context, _vm->getResourceDescription()->psychicProfileResourceId, resourceData, resourceDataLength);
+ _vm->_resource->loadResource(context, _vm->getResourceDescription()->psychicProfileResourceId, resourceData);
- byte *buf;
- size_t buflen;
+ ByteArray image;
int width;
int height;
- _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height);
+ _vm->decodeBGImage(resourceData, image, &width, &height);
- const PalEntry *palette = (const PalEntry *)_vm->getImagePal(resourceData, resourceDataLength);
+ const PalEntry *palette = (const PalEntry *)_vm->getImagePal(resourceData);
const Rect profileRect(width, height);
- _vm->_render->getBackGroundSurface()->blit(profileRect, buf);
+ _vm->_render->getBackGroundSurface()->blit(profileRect, image.getBuffer());
_vm->_render->addDirtyRect(profileRect);
_vm->_frameCount++;
_vm->_gfx->setPalette(palette);
- free(buf);
- free(resourceData);
-
// Draw the scene. It won't be drawn by Render::drawScene(), as a placard is up
_vm->_scene->draw();
}
@@ -570,122 +559,74 @@ int Events::handleInterval(Event *event) {
return kEvStDelete;
}
-// Schedules an event in the event list; returns a pointer to the scheduled
-// event suitable for chaining if desired.
-Event *Events::queue(Event *event) {
- Event *queuedEvent;
-
- _eventList.push_back(*event);
- queuedEvent = &*--_eventList.end();
- initializeEvent(queuedEvent);
-
- return queuedEvent;
-}
-
-// Places a 'add_event' on the end of an event chain given by 'head_event'
-// (head_event may be in any position in the event chain)
-Event *Events::chain(Event *headEvent, Event *addEvent) {
- if (headEvent == NULL) {
- return queue(addEvent);
- }
+EventColumns *Events::chain(EventColumns *eventColumns, const Event &event) {
+
+ if (eventColumns == NULL) {
+ EventColumns tmp;
- Event *walkEvent;
- for (walkEvent = headEvent; walkEvent->chain != NULL; walkEvent = walkEvent->chain) {
- continue;
+ _eventList.push_back(tmp);
+ eventColumns = &_eventList.back();
}
- walkEvent->chain = (Event *)malloc(sizeof(*walkEvent->chain));
- *walkEvent->chain = *addEvent;
- initializeEvent(walkEvent->chain);
+ eventColumns->push_back(event);
+ initializeEvent(eventColumns->back());
- return walkEvent->chain;
+ return eventColumns;
}
-int Events::initializeEvent(Event *event) {
- event->chain = NULL;
- switch (event->type) {
+void Events::initializeEvent(Event &event) {
+ switch (event.type) {
case kEvTOneshot:
break;
case kEvTContinuous:
case kEvTImmediate:
- event->time += event->duration;
+ event.time += event.duration;
break;
case kEvTInterval:
break;
- default:
- return FAILURE;
}
-
- return SUCCESS;
}
-int Events::clearList(bool playQueuedMusic) {
- Event *chain_walk;
- Event *next_chain;
-
+void Events::clearList(bool playQueuedMusic) {
// Walk down event list
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
// Only remove events not marked kEvFNoDestory (engine events)
- if (!(eventi->code & kEvFNoDestory)) {
+ if (!(eventi->front().code & kEvFNoDestory)) {
// Handle queued music change events before deleting them
// This can happen in IHNM by music events set by sfQueueMusic()
// Fixes bug #2057987 - "IHNM: Music stops in Ellen's chapter"
- if (playQueuedMusic && ((eventi->code & EVENT_MASK) == kMusicEvent)) {
+ if (playQueuedMusic && ((eventi->front().code & EVENT_MASK) == kMusicEvent)) {
_vm->_music->stop();
- if (eventi->op == kEventPlay)
- _vm->_music->play(eventi->param, (MusicFlags)eventi->param2);
+ if (eventi->front().op == kEventPlay)
+ _vm->_music->play(eventi->front().param, (MusicFlags)eventi->front().param2);
}
- // Remove any events chained off this one
- for (chain_walk = eventi->chain; chain_walk != NULL; chain_walk = next_chain) {
- next_chain = chain_walk->chain;
- free(chain_walk);
- }
eventi = _eventList.reverse_erase(eventi);
}
}
-
- return SUCCESS;
}
// Removes all events from the list (even kEvFNoDestory)
-int Events::freeList() {
- Event *chain_walk;
- Event *next_chain;
-
- // Walk down event list
- EventList::iterator eventi = _eventList.begin();
- while (eventi != _eventList.end()) {
-
- // Remove any events chained off this one */
- for (chain_walk = eventi->chain; chain_walk != NULL; chain_walk = next_chain) {
- next_chain = chain_walk->chain;
- free(chain_walk);
- }
- eventi = _eventList.erase(eventi);
- }
-
- return SUCCESS;
+void Events::freeList() {
+ _eventList.clear();
}
// Walks down the event list, updating event times by 'msec'.
-int Events::processEventTime(long msec) {
+void Events::processEventTime(long msec) {
uint16 event_count = 0;
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
- eventi->time -= msec;
+ eventi->front().time -= msec;
event_count++;
- if (eventi->type == kEvTImmediate)
+ if (eventi->front().type == kEvTImmediate)
break;
if (event_count > EVENT_WARNINGCOUNT) {
warning("Event list exceeds %u", EVENT_WARNINGCOUNT);
}
}
-
- return SUCCESS;
}
} // End of namespace Saga
diff --git a/engines/saga/events.h b/engines/saga/events.h
index d1530787c2..135c0beb55 100644
--- a/engines/saga/events.h
+++ b/engines/saga/events.h
@@ -142,13 +142,14 @@ struct Event {
long duration; // Duration of event
long d_reserved;
- Event *chain; // Event chain (For consecutive events)
Event() {
memset(this, 0, sizeof(*this));
}
};
-typedef Common::List<Event> EventList;
+typedef Common::List<Event> EventColumns;
+
+typedef Common::List<EventColumns> EventList;
#define EVENT_WARNINGCOUNT 1000
#define EVENT_MASK 0x00FF
@@ -164,19 +165,26 @@ class Events {
public:
Events(SagaEngine *vm);
~Events();
- int handleEvents(long msec);
- int clearList(bool playQueuedMusic = true);
- int freeList();
- Event *queue(Event *event);
- Event *chain(Event *headEvent, Event *addEvent);
+ void handleEvents(long msec);
+ void clearList(bool playQueuedMusic = true);
+ void freeList();
+
+ // Schedules an event in the event list; returns a pointer to the scheduled
+ // event columns suitable for chaining if desired.
+ EventColumns *queue(const Event &event) {
+ return chain(NULL, event);
+ }
+
+ // Places a 'event' on the end of an event columns given by 'eventColumns'
+ EventColumns *chain(EventColumns *eventColumns, const Event &event);
private:
int handleContinuous(Event *event);
int handleOneShot(Event *event);
int handleInterval(Event *event);
int handleImmediate(Event *event);
- int processEventTime(long msec);
- int initializeEvent(Event *event);
+ void processEventTime(long msec);
+ void initializeEvent(Event &event);
private:
SagaEngine *_vm;
diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
index 47f1a122c0..01e74d2984 100644
--- a/engines/saga/font.cpp
+++ b/engines/saga/font.cpp
@@ -41,11 +41,9 @@ Font::Font(SagaEngine *vm) : _vm(vm) {
assert(_vm->getFontsCount() > 0);
- _fonts = (FontData **)calloc(_vm->getFontsCount(), sizeof(*_fonts));
- _loadedFonts = 0;
-
+ _fonts.resize(_vm->getFontsCount());
for (i = 0; i < _vm->getFontsCount(); i++) {
- loadFont(_vm->getFontDescription(i)->fontResourceId);
+ loadFont(&_fonts[i], _vm->getFontDescription(i)->fontResourceId);
}
_fontMapping = 0;
@@ -53,25 +51,11 @@ Font::Font(SagaEngine *vm) : _vm(vm) {
Font::~Font() {
debug(8, "Font::~Font(): Freeing fonts.");
- int i;
-
- for (i = 0 ; i < _loadedFonts ; i++) {
- if (_fonts[i] != NULL) {
- free(_fonts[i]->normal.font);
- free(_fonts[i]->outline.font);
- }
-
- free(_fonts[i]);
- }
-
- free(_fonts);
}
-void Font::loadFont(uint32 fontResourceId) {
- FontData *font;
- byte *fontResourcePointer;
- size_t fontResourceLength;
+void Font::loadFont(FontData *font, uint32 fontResourceId) {
+ ByteArray fontResourceData;
int numBits;
int c;
ResourceContext *fontContext;
@@ -84,16 +68,13 @@ void Font::loadFont(uint32 fontResourceId) {
}
// Load font resource
- _vm->_resource->loadResource(fontContext, fontResourceId, fontResourcePointer, fontResourceLength);
+ _vm->_resource->loadResource(fontContext, fontResourceId, fontResourceData);
- if (fontResourceLength < FONT_DESCSIZE) {
- error("Font::loadFont() Invalid font length (%i < %i)", (int)fontResourceLength, FONT_DESCSIZE);
+ if (fontResourceData.size() < FONT_DESCSIZE) {
+ error("Font::loadFont() Invalid font length (%i < %i)", (int)fontResourceData.size(), FONT_DESCSIZE);
}
- MemoryReadStreamEndian readS(fontResourcePointer, fontResourceLength, fontContext->isBigEndian());
-
- // Create new font structure
- font = (FontData *)malloc(sizeof(*font));
+ ByteArrayReadStreamEndian readS(fontResourceData, fontContext->isBigEndian());
// Read font header
font->normal.header.charHeight = readS.readUint16();
@@ -126,17 +107,12 @@ void Font::loadFont(uint32 fontResourceId) {
error("Invalid font resource size");
}
- font->normal.font = (byte*)malloc(fontResourceLength - FONT_DESCSIZE);
- memcpy(font->normal.font, fontResourcePointer + FONT_DESCSIZE, fontResourceLength - FONT_DESCSIZE);
-
- free(fontResourcePointer);
+ font->normal.font.resize(fontResourceData.size() - FONT_DESCSIZE);
+ memcpy(font->normal.font.getBuffer(), fontResourceData.getBuffer() + FONT_DESCSIZE, fontResourceData.size() - FONT_DESCSIZE);
// Create outline font style
createOutline(font);
-
- // Set font data
- _fonts[_loadedFonts++] = font;
}
void Font::createOutline(FontData *font) {
@@ -145,12 +121,12 @@ void Font::createOutline(FontData *font) {
int newByteWidth;
int newRowLength = 0;
int currentByte;
- unsigned char *basePointer;
- unsigned char *srcPointer;
- unsigned char *destPointer1;
- unsigned char *destPointer2;
- unsigned char *destPointer3;
- unsigned char charRep;
+ byte *basePointer;
+ byte *srcPointer;
+ byte *destPointer1;
+ byte *destPointer2;
+ byte *destPointer3;
+ byte charRep;
// Populate new font style character data
for (i = 0; i < FONT_CHARCOUNT; i++) {
@@ -177,20 +153,20 @@ void Font::createOutline(FontData *font) {
font->outline.header.rowLength = newRowLength;
// Allocate new font representation storage
- font->outline.font = (unsigned char *)calloc(newRowLength, font->outline.header.charHeight);
+ font->outline.font.resize(newRowLength * font->outline.header.charHeight);
// Generate outline font representation
for (i = 0; i < FONT_CHARCOUNT; i++) {
for (row = 0; row < font->normal.header.charHeight; row++) {
for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
- basePointer = font->outline.font + font->outline.fontCharEntry[i].index + currentByte;
+ basePointer = &font->outline.font[font->outline.fontCharEntry[i].index + currentByte];
destPointer1 = basePointer + newRowLength * row;
destPointer2 = basePointer + newRowLength * (row + 1);
destPointer3 = basePointer + newRowLength * (row + 2);
if (currentByte > 0) {
// Get last two columns from previous byte
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1);
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
charRep = *srcPointer;
*destPointer1 |= ((charRep << 6) | (charRep << 7));
*destPointer2 |= ((charRep << 6) | (charRep << 7));
@@ -198,7 +174,7 @@ void Font::createOutline(FontData *font) {
}
if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte;
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
charRep = *srcPointer;
*destPointer1 |= charRep | (charRep >> 1) | (charRep >> 2);
*destPointer2 |= charRep | (charRep >> 1) | (charRep >> 2);
@@ -210,15 +186,15 @@ void Font::createOutline(FontData *font) {
// "Hollow out" character to prevent overdraw
for (row = 0; row < font->normal.header.charHeight; row++) {
for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
- destPointer2 = font->outline.font + font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte;
+ destPointer2 = &font->outline.font[font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte];
if (currentByte > 0) {
// Get last two columns from previous byte
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1);
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
*destPointer2 &= ((*srcPointer << 7) ^ 0xFFU);
}
if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte;
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
*destPointer2 &= ((*srcPointer >> 1) ^ 0xFFU);
}
}
@@ -289,7 +265,7 @@ void Font::draw(FontId fontId, const char *text, size_t count, const Common::Poi
void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags) {
const byte *textPointer;
- byte *c_dataPointer;
+ const byte *c_dataPointer;
int c_code;
int charRow = 0;
Point textPoint(point);
@@ -384,7 +360,7 @@ void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, co
break;
}
- c_dataPointer = drawFont.font + charRow * drawFont.header.rowLength + drawFont.fontCharEntry[c_code].index;
+ c_dataPointer = &drawFont.font[charRow * drawFont.header.rowLength + drawFont.fontCharEntry[c_code].index];
for (c_byte = 0; c_byte < c_byte_len; c_byte++, c_dataPointer++) {
// Check each bit, draw pixel if bit is set
diff --git a/engines/saga/font.h b/engines/saga/font.h
index 1b9f290a1b..6f66545756 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -120,7 +120,7 @@ struct FontCharEntry {
struct FontStyle {
FontHeader header;
FontCharEntry fontCharEntry[256];
- byte *font;
+ ByteArray font;
};
struct FontData {
@@ -170,14 +170,14 @@ class Font {
void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags);
void textDraw(FontId fontId, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
- void loadFont(uint32 fontResourceId);
+ void loadFont(FontData *font, uint32 fontResourceId);
void createOutline(FontData *font);
void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
void outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags);
FontData *getFont(FontId fontId) {
validate(fontId);
- return _fonts[fontId];
+ return &_fonts[fontId];
}
int getHeight(FontId fontId) {
@@ -190,7 +190,7 @@ class Font {
}
}
bool valid(FontId fontId) {
- return (fontId < _loadedFonts);
+ return (uint(fontId) < _fonts.size());
}
int getByteLen(int numBits) const {
int byteLength = numBits / 8;
@@ -207,8 +207,7 @@ class Font {
int _fontMapping;
- int _loadedFonts;
- FontData **_fonts;
+ Common::Array<FontData> _fonts;
};
} // End of namespace Saga
diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp
index 40a633ac5d..77842acc7b 100644
--- a/engines/saga/gfx.cpp
+++ b/engines/saga/gfx.cpp
@@ -173,13 +173,11 @@ void Gfx::initPalette() {
error("Resource::loadGlobalResources() resource context not found");
}
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
- _vm->_resource->loadResource(resourceContext, RID_IHNM_DEFAULT_PALETTE,
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_DEFAULT_PALETTE, resourceData);
- MemoryReadStream metaS(resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian metaS(resourceData);
for (int i = 0; i < 256; i++) {
_globalPalette[i].red = metaS.readByte();
@@ -187,8 +185,6 @@ void Gfx::initPalette() {
_globalPalette[i].blue = metaS.readByte();
}
- free(resourcePointer);
-
setPalette(_globalPalette, true);
}
@@ -504,22 +500,19 @@ void Gfx::setCursor(CursorType cursorType) {
break;
}
- byte *resource;
- size_t resourceLength;
- byte *image;
- size_t imageLength;
+ ByteArray resourceData;
+ ByteArray image;
int width, height;
if (resourceId != (uint32)-1) {
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- _vm->_resource->loadResource(context, resourceId, resource, resourceLength);
+ _vm->_resource->loadResource(context, resourceId, resourceData);
- _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &width, &height);
+ _vm->decodeBGImage(resourceData, image, &width, &height);
} else {
- resource = NULL;
width = height = 31;
- image = (byte *)calloc(width, height);
+ image.resize(width * height);
for (int i = 0; i < 14; i++) {
image[15 * 31 + i] = 1;
@@ -530,10 +523,7 @@ void Gfx::setCursor(CursorType cursorType) {
}
// Note: Hard-coded hotspot
- CursorMan.replaceCursor(image, width, height, 15, 15, 0);
-
- free(image);
- free(resource);
+ CursorMan.replaceCursor(image.getBuffer(), width, height, 15, 15, 0);
}
}
@@ -564,8 +554,9 @@ bool hitTestPoly(const Point *points, unsigned int npoints, const Point& test_po
// This method adds a dirty rectangle automatically
void Gfx::drawFrame(const Common::Point &p1, const Common::Point &p2, int color) {
- _backBuffer.drawFrame(p1, p2, color);
- _vm->_render->addDirtyRect(Common::Rect(p1.x, p1.y, p2.x + 1, p2.y + 1));
+ Common::Rect rect(MIN(p1.x, p2.x), MIN(p1.y, p2.y), MAX(p1.x, p2.x) + 1, MAX(p1.y, p2.y) + 1);
+ _backBuffer.frameRect(rect, color);
+ _vm->_render->addDirtyRect(rect);
}
// This method adds a dirty rectangle automatically
diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h
index f3ccad469f..18d88503ce 100644
--- a/engines/saga/gfx.h
+++ b/engines/saga/gfx.h
@@ -108,10 +108,7 @@ struct Surface : Graphics::Surface {
rect.right = w;
rect.bottom = h;
}
- void drawFrame(const Common::Point &p1, const Common::Point &p2, int color) {
- Common::Rect rect(MIN(p1.x, p2.x), MIN(p1.y, p2.y), MAX(p1.x, p2.x) + 1, MAX(p1.y, p2.y) + 1);
- frameRect(rect, color);
- }
+
void drawRect(const Common::Rect &destRect, int color) {
Common::Rect rect(w , h);
rect.clip(destRect);
@@ -198,7 +195,7 @@ public:
// WARNING: This method does not add a dirty rectangle automatically.
// Whenever it gets called, the corresponding caller must take care
// to add the corresponding dirty rectangle itself
- void drawPolyLine(Common::Point *points, int count, int color) {
+ void drawPolyLine(const Common::Point *points, int count, int color) {
_backBuffer.drawPolyLine(points, count, color);
}
diff --git a/engines/saga/image.cpp b/engines/saga/image.cpp
index 7d8eb83550..87d9e514c8 100644
--- a/engines/saga/image.cpp
+++ b/engines/saga/image.cpp
@@ -47,22 +47,18 @@ static int granulate(int value, int granularity) {
}
}
-int SagaEngine::decodeBGImage(const byte *image_data, size_t image_size,
- byte **output_buf, size_t *output_buf_len, int *w, int *h, bool flip) {
+bool SagaEngine::decodeBGImage(const ByteArray &imageData, ByteArray &outputBuffer, int *w, int *h, bool flip) {
ImageHeader hdr;
int modex_height;
const byte *RLE_data_ptr;
size_t RLE_data_len;
- byte *decode_buf;
- size_t decode_buf_len;
- byte *out_buf;
- size_t out_buf_len;
+ ByteArray decodeBuffer;
- if (image_size <= SAGA_IMAGE_DATA_OFFSET) {
- error("decodeBGImage() Image size is way too small (%d)", (int)image_size);
+ if (imageData.size() <= SAGA_IMAGE_DATA_OFFSET) {
+ error("decodeBGImage() Image size is way too small (%d)", (int)imageData.size());
}
- MemoryReadStreamEndian readS(image_data, image_size, isBigEndian());
+ ByteArrayReadStreamEndian readS(imageData, isBigEndian());
hdr.width = readS.readUint16();
hdr.height = readS.readUint16();
@@ -70,45 +66,36 @@ int SagaEngine::decodeBGImage(const byte *image_data, size_t image_size,
readS.readUint16();
readS.readUint16();
- RLE_data_ptr = image_data + SAGA_IMAGE_DATA_OFFSET;
- RLE_data_len = image_size - SAGA_IMAGE_DATA_OFFSET;
+ RLE_data_ptr = &imageData.front() + SAGA_IMAGE_DATA_OFFSET;
+ RLE_data_len = imageData.size() - SAGA_IMAGE_DATA_OFFSET;
modex_height = granulate(hdr.height, 4);
- decode_buf_len = hdr.width * modex_height;
- decode_buf = (byte *)malloc(decode_buf_len);
+ decodeBuffer.resize(hdr.width * modex_height);
- out_buf_len = hdr.width * hdr.height;
- out_buf = (byte *)malloc(out_buf_len);
+ outputBuffer.resize(hdr.width * hdr.height);
- if (decodeBGImageRLE(RLE_data_ptr,
- RLE_data_len, decode_buf, decode_buf_len) != SUCCESS) {
- free(decode_buf);
- free(out_buf);
- return FAILURE;
+ if (!decodeBGImageRLE(RLE_data_ptr, RLE_data_len, decodeBuffer)) {
+ return false;
}
- unbankBGImage(out_buf, decode_buf, hdr.width, hdr.height);
+ unbankBGImage(outputBuffer.getBuffer(), decodeBuffer.getBuffer(), hdr.width, hdr.height);
// For some reason bg images in IHNM are upside down
if (getGameId() == GID_IHNM && !flip) {
- flipImage(out_buf, hdr.width, hdr.height);
+ flipImage(outputBuffer.getBuffer(), hdr.width, hdr.height);
}
- free(decode_buf);
-
- *output_buf_len = out_buf_len;
- *output_buf = out_buf;
-
*w = hdr.width;
*h = hdr.height;
- return SUCCESS;
+ return true;
}
-int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outbuf, size_t outbuf_len) {
+bool SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, ByteArray &outbuf) {
const byte *inbuf_ptr;
byte *outbuf_ptr;
+ byte *outbuf_start;
uint32 inbuf_remain;
const byte *inbuf_end;
@@ -134,18 +121,18 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
inbuf_ptr = inbuf;
inbuf_remain = inbuf_len;
- outbuf_ptr = outbuf;
- outbuf_remain = outbuf_len;
+ outbuf_start = outbuf_ptr = outbuf.getBuffer();
+ outbuf_remain = outbuf.size();
+ outbuf_end = (outbuf_start + outbuf_remain) - 1;
+ memset(outbuf_start, 0, outbuf_remain);
inbuf_end = (inbuf + inbuf_len) - 1;
- outbuf_end = (outbuf + outbuf_len) - 1;
- memset(outbuf, 0, outbuf_len);
while ((inbuf_remain > 1) && (outbuf_remain > 0) && !decode_err) {
if ((inbuf_ptr > inbuf_end) || (outbuf_ptr > outbuf_end)) {
- return FAILURE;
+ return false;
}
mark_byte = *inbuf_ptr++;
@@ -158,7 +145,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Uncompressed run follows: Max runlength 63
runcount = mark_byte & 0x3f;
if ((inbuf_remain < runcount) || (outbuf_remain < runcount)) {
- return FAILURE;
+ return false;
}
for (c = 0; c < runcount; c++) {
@@ -173,7 +160,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Compressed run follows: Max runlength 63
runcount = (mark_byte & 0x3f) + 3;
if (!inbuf_remain || (outbuf_remain < runcount)) {
- return FAILURE;
+ return false;
}
for (c = 0; c < runcount; c++) {
@@ -194,8 +181,8 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
runcount = ((mark_byte >> 3) & 0x07U) + 3;
backtrack_amount = *inbuf_ptr;
- if (!inbuf_remain || (backtrack_amount > (outbuf_ptr - outbuf)) || (runcount > outbuf_remain)) {
- return FAILURE;
+ if (!inbuf_remain || (backtrack_amount > (outbuf_ptr - outbuf_start)) || (runcount > outbuf_remain)) {
+ return false;
}
inbuf_ptr++;
@@ -224,7 +211,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
runcount = (mark_byte & 0x0F) + 1;
if ((inbuf_remain < (runcount + 2)) || (outbuf_remain < (runcount * 8))) {
- return FAILURE;
+ return false;
}
bitfield_byte1 = *inbuf_ptr++;
@@ -252,7 +239,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Uncompressed run follows
runcount = ((mark_byte & 0x0F) << 8) + *inbuf_ptr;
if ((inbuf_remain < (runcount + 1)) || (outbuf_remain < runcount)) {
- return FAILURE;
+ return false;
}
inbuf_ptr++;
@@ -271,14 +258,14 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Repeat decoded sequence from output stream
backtrack_amount = ((mark_byte & 0x0F) << 8) + *inbuf_ptr;
if (inbuf_remain < 2) {
- return FAILURE;
+ return false;
}
inbuf_ptr++;
runcount = *inbuf_ptr++;
- if ((backtrack_amount > (outbuf_ptr - outbuf)) || (outbuf_remain < runcount)) {
- return FAILURE;
+ if ((backtrack_amount > (outbuf_ptr - outbuf_start)) || (outbuf_remain < runcount)) {
+ return false;
}
backtrack_ptr = outbuf_ptr - backtrack_amount;
@@ -292,44 +279,42 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
continue;
break;
default:
- return FAILURE;
+ return false;
}
}
- return SUCCESS;
+ return true;
}
-int SagaEngine::flipImage(byte *img_buf, int columns, int scanlines) {
+void SagaEngine::flipImage(byte *imageBuffer, int columns, int scanlines) {
int line;
- byte *tmp_scan;
+ ByteArray tmp_scan;
byte *flip_p1;
byte *flip_p2;
+ byte *flip_tmp;
int flipcount = scanlines / 2;
- tmp_scan = (byte *)malloc(columns);
- if (tmp_scan == NULL) {
- return FAILURE;
+ tmp_scan.resize(columns);
+ flip_tmp = tmp_scan.getBuffer();
+ if (flip_tmp == NULL) {
+ return;
}
- flip_p1 = img_buf;
- flip_p2 = img_buf + (columns * (scanlines - 1));
+ flip_p1 = imageBuffer;
+ flip_p2 = imageBuffer + (columns * (scanlines - 1));
for (line = 0; line < flipcount; line++) {
- memcpy(tmp_scan, flip_p1, columns);
+ memcpy(flip_tmp, flip_p1, columns);
memcpy(flip_p1, flip_p2, columns);
- memcpy(flip_p2, tmp_scan, columns);
+ memcpy(flip_p2, flip_tmp, columns);
flip_p1 += columns;
flip_p2 -= columns;
}
-
- free(tmp_scan);
-
- return SUCCESS;
}
-int SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, int scanlines) {
+void SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, int scanlines) {
int x, y;
int temp;
int quadruple_rows;
@@ -424,15 +409,6 @@ int SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, i
default:
break;
}
- return SUCCESS;
-}
-
-const byte *SagaEngine::getImagePal(const byte *image_data, size_t image_size) {
- if (image_size <= SAGA_IMAGE_HEADER_LEN) {
- return NULL;
- }
-
- return image_data + SAGA_IMAGE_HEADER_LEN;
}
} // End of namespace Saga
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index a77ec1c140..5b15fc9803 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -124,8 +124,7 @@ static const int IHNMTextStringIdsLUT[56] = {
#define buttonRes1 0x42544E01
Interface::Interface(SagaEngine *vm) : _vm(vm) {
- byte *resource;
- size_t resourceLength;
+ ByteArray resourceData;
int i;
#if 0
@@ -170,34 +169,27 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
}
}
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_mainPanel.image,
- &_mainPanel.imageLength, &_mainPanel.imageWidth, &_mainPanel.imageHeight);
-
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _mainPanel.image, &_mainPanel.imageWidth, &_mainPanel.imageHeight);
// Converse panel
_conversePanel.buttons = _vm->getDisplayInfo().conversePanelButtons;
_conversePanel.buttonsCount = _vm->getDisplayInfo().conversePanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_conversePanel.image,
- &_conversePanel.imageLength, &_conversePanel.imageWidth, &_conversePanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _conversePanel.image, &_conversePanel.imageWidth, &_conversePanel.imageHeight);
// Option panel
if (!_vm->_script->isNonInteractiveDemo()) {
_optionPanel.buttons = _vm->getDisplayInfo().optionPanelButtons;
_optionPanel.buttonsCount = _vm->getDisplayInfo().optionPanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_optionPanel.image,
- &_optionPanel.imageLength, &_optionPanel.imageWidth, &_optionPanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _optionPanel.image, &_optionPanel.imageWidth, &_optionPanel.imageHeight);
} else {
_optionPanel.buttons = NULL;
_optionPanel.buttonsCount = 0;
- _optionPanel.sprites.spriteCount = 0;
+ _optionPanel.sprites.clear();
}
#ifdef ENABLE_IHNM
@@ -206,10 +198,8 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons;
_quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_quitPanel.image,
- &_quitPanel.imageLength, &_quitPanel.imageWidth, &_quitPanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _quitPanel.image, &_quitPanel.imageWidth, &_quitPanel.imageHeight);
}
// Save panel
@@ -217,10 +207,8 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_savePanel.buttons = _vm->getDisplayInfo().savePanelButtons;
_savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_savePanel.image,
- &_savePanel.imageLength, &_savePanel.imageWidth, &_savePanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _savePanel.image, &_savePanel.imageWidth, &_savePanel.imageHeight);
}
// Load panel
@@ -228,10 +216,8 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons;
_loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_loadPanel.image,
- &_loadPanel.imageLength, &_loadPanel.imageWidth, &_loadPanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _loadPanel.image, &_loadPanel.imageWidth, &_loadPanel.imageHeight);
}
#endif
@@ -323,16 +309,12 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_inventoryStart = 0;
_inventoryEnd = 0;
_inventoryBox = 0;
- _inventorySize = ITE_INVENTORY_SIZE;
_saveReminderState = 0;
_optionSaveFileTop = 0;
_optionSaveFileTitleNumber = 0;
- _inventory = (uint16 *)calloc(_inventorySize, sizeof(uint16));
- if (_inventory == NULL) {
- error("Interface::Interface(): not enough memory");
- }
+ _inventory.resize(ITE_INVENTORY_SIZE);
_textInput = false;
_statusTextInput = false;
@@ -345,25 +327,6 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
}
Interface::~Interface() {
- free(_inventory);
-
- free(_mainPanel.image);
- free(_conversePanel.image);
- free(_optionPanel.image);
- free(_quitPanel.image);
- free(_loadPanel.image);
- free(_savePanel.image);
-
- _mainPanel.sprites.freeMem();
- _conversePanel.sprites.freeMem();
- _optionPanel.sprites.freeMem();
- _quitPanel.sprites.freeMem();
- _loadPanel.sprites.freeMem();
- _savePanel.sprites.freeMem();
- _protectPanel.sprites.freeMem();
-
- _defPortraits.freeMem();
- _scenePortraits.freeMem();
}
void Interface::saveReminderCallback(void *refCon) {
@@ -768,7 +731,7 @@ void Interface::setStatusText(const char *text, int statusColor) {
}
void Interface::loadScenePortraits(int resourceId) {
- _scenePortraits.freeMem();
+ _scenePortraits.clear();
_vm->_sprite->loadList(resourceId, _scenePortraits);
}
@@ -817,7 +780,7 @@ void Interface::draw() {
if (_panelMode == kPanelMain || _panelMode == kPanelMap ||
(_panelMode == kPanelNull && _vm->isIHNMDemo())) {
_mainPanel.getRect(rect);
- _vm->_gfx->drawRegion(rect, _mainPanel.image);
+ _vm->_gfx->drawRegion(rect, _mainPanel.image.getBuffer());
for (int i = 0; i < kVerbTypeIdsMax; i++) {
if (_verbTypeToPanelButton[i] != NULL) {
@@ -826,7 +789,7 @@ void Interface::draw() {
}
} else if (_panelMode == kPanelConverse) {
_conversePanel.getRect(rect);
- _vm->_gfx->drawRegion(rect, _conversePanel.image);
+ _vm->_gfx->drawRegion(rect, _conversePanel.image.getBuffer());
converseDisplayTextLines();
}
@@ -847,7 +810,7 @@ void Interface::draw() {
// can tell this is what the original engine does. And it keeps
// ITE from crashing when entering the Elk King's court.
- if (_rightPortrait >= _scenePortraits.spriteCount)
+ if (_rightPortrait >= (int)_scenePortraits.size())
_rightPortrait = 0;
_vm->_sprite->draw(_scenePortraits, _rightPortrait, rightPortraitPoint, 256);
@@ -960,7 +923,7 @@ void Interface::drawOption() {
int spritenum = 0;
_optionPanel.getRect(rect);
- _vm->_gfx->drawRegion(rect, _optionPanel.image);
+ _vm->_gfx->drawRegion(rect, _optionPanel.image.getBuffer());
for (int i = 0; i < _optionPanel.buttonsCount; i++) {
panelButton = &_optionPanel.buttons[i];
@@ -1037,7 +1000,7 @@ void Interface::drawQuit() {
if (_vm->getGameId() == GID_ITE)
drawButtonBox(rect, kButton, false);
else
- _vm->_gfx->drawRegion(rect, _quitPanel.image);
+ _vm->_gfx->drawRegion(rect, _quitPanel.image.getBuffer());
for (i = 0; i < _quitPanel.buttonsCount; i++) {
panelButton = &_quitPanel.buttons[i];
@@ -1103,7 +1066,7 @@ void Interface::drawLoad() {
if (_vm->getGameId() == GID_ITE)
drawButtonBox(rect, kButton, false);
else
- _vm->_gfx->drawRegion(rect, _loadPanel.image);
+ _vm->_gfx->drawRegion(rect, _loadPanel.image.getBuffer());
for (i = 0; i < _loadPanel.buttonsCount; i++) {
panelButton = &_loadPanel.buttons[i];
@@ -1323,7 +1286,7 @@ void Interface::drawSave() {
if (_vm->getGameId() == GID_ITE)
drawButtonBox(rect, kButton, false);
else
- _vm->_gfx->drawRegion(rect, _savePanel.image);
+ _vm->_gfx->drawRegion(rect, _savePanel.image.getBuffer());
for (i = 0; i < _savePanel.buttonsCount; i++) {
panelButton = &_savePanel.buttons[i];
@@ -1587,7 +1550,7 @@ void Interface::handleChapterSelectionClick(const Point& mousePoint) {
event.param4 = obj; // Object
event.param5 = 0; // With Object
event.param6 = obj; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
}
}
@@ -2064,7 +2027,7 @@ void Interface::updateInventory(int pos) {
}
void Interface::addToInventory(int objectId) {
- if (_inventoryCount >= _inventorySize) {
+ if (uint(_inventoryCount) >= _inventory.size()) {
return;
}
@@ -2281,14 +2244,22 @@ void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelBut
}
break;
case kTextMusic:
- if (_vm->_musicVolume)
+ if (_vm->_musicVolume) {
textId = kText10Percent + _vm->_musicVolume / 25 - 1;
+ if (textId > kTextMax) {
+ textId = kTextMax;
+ }
+ }
else
textId = kTextOff;
break;
case kTextSound:
- if (_vm->_soundVolume)
+ if (_vm->_soundVolume) {
textId = kText10Percent + _vm->_soundVolume / 25 - 1;
+ if (textId > kTextMax) {
+ textId = kTextMax;
+ }
+ }
else
textId = kTextOff;
break;
@@ -2423,18 +2394,9 @@ void Interface::drawVerbPanelText(PanelButton *panelButton, KnownColor textKnown
// Converse stuff
-void Interface::converseInit() {
- for (int i = 0; i < CONVERSE_MAX_TEXTS; i++)
- _converseText[i].text = NULL;
- converseClear();
-}
-
void Interface::converseClear() {
for (int i = 0; i < CONVERSE_MAX_TEXTS; i++) {
- if (_converseText[i].text != NULL) {
- free(_converseText[i].text);
- _converseText[i].text = NULL;
- }
+ _converseText[i].text.clear();
_converseText[i].stringNum = -1;
_converseText[i].replyId = 0;
_converseText[i].replyFlags = 0;
@@ -2480,8 +2442,8 @@ bool Interface::converseAddText(const char *text, int strId, int replyId, byte r
return true;
}
- _converseText[_converseTextCount].text = (char *)malloc(i + 1);
- strncpy(_converseText[_converseTextCount].text, _converseWorkString, i);
+ _converseText[_converseTextCount].text.resize(i + 1);
+ strncpy(&_converseText[_converseTextCount].text.front(), _converseWorkString, i);
_converseText[_converseTextCount].strId = strId;
_converseText[_converseTextCount].text[i] = 0;
@@ -2591,7 +2553,7 @@ void Interface::converseDisplayTextLines() {
rect.left += 8;
_vm->_gfx->drawRect(rect, backgnd);
- str = _converseText[relPos].text;
+ str = &_converseText[relPos].text.front();
if (_converseText[relPos].textNum == 0) { // first entry
textPoint.x = rect.left - 6;
@@ -2725,10 +2687,9 @@ void Interface::loadState(Common::InSaveFile *in) {
void Interface::mapPanelShow() {
int i;
- byte *resource;
- size_t resourceLength, imageLength;
+ ByteArray resourceData;
Rect rect;
- byte *image;
+ ByteArray image;
int imageWidth, imageHeight;
const byte *pal;
PalEntry cPal[PAL_ENTRIES];
@@ -2737,9 +2698,8 @@ void Interface::mapPanelShow() {
rect.left = rect.top = 0;
- _vm->_resource->loadResource(_interfaceContext,
- _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resource, resourceLength);
- if (resourceLength == 0) {
+ _vm->_resource->loadResource(_interfaceContext, _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resourceData);
+ if (resourceData.empty()) {
error("Interface::mapPanelShow() unable to load Tycho map resource");
}
@@ -2753,8 +2713,8 @@ void Interface::mapPanelShow() {
_vm->_render->setFlag(RF_MAP);
- _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &imageWidth, &imageHeight);
- pal = _vm->getImagePal(resource, resourceLength);
+ _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight);
+ pal = _vm->getImagePal(resourceData);
for (i = 0; i < PAL_ENTRIES; i++) {
cPal[i].red = *pal++;
@@ -2765,7 +2725,7 @@ void Interface::mapPanelShow() {
rect.setWidth(imageWidth);
rect.setHeight(imageHeight);
- _vm->_gfx->drawRegion(rect, image);
+ _vm->_gfx->drawRegion(rect, image.getBuffer());
// Evil Evil
for (i = 0; i < 6 ; i++) {
@@ -2774,8 +2734,6 @@ void Interface::mapPanelShow() {
_vm->_system->delayMillis(5);
}
- free(resource);
- free(image);
setSaveReminderState(false);
@@ -2832,10 +2790,9 @@ void Interface::keyBoss() {
_vm->_music->pause();
int i;
- byte *resource;
- size_t resourceLength, imageLength;
+ ByteArray resourceData;
Rect rect;
- byte *image;
+ ByteArray image;
int imageWidth, imageHeight;
const byte *pal;
PalEntry cPal[PAL_ENTRIES];
@@ -2844,20 +2801,20 @@ void Interface::keyBoss() {
rect.left = rect.top = 0;
- _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resource, resourceLength);
- if (resourceLength == 0) {
+ _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resourceData);
+ if (resourceData.empty()) {
error("Interface::bossKey() unable to load Boss image resource");
}
_bossMode = _panelMode;
setMode(kPanelBoss);
- _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &imageWidth, &imageHeight);
+ _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight);
rect.setWidth(imageWidth);
rect.setHeight(imageHeight);
_vm->_gfx->getCurrentPal(_mapSavedPal);
- pal = _vm->getImagePal(resource, resourceLength);
+ pal = _vm->getImagePal(resourceData);
cPal[0].red = 0;
cPal[0].green = 0;
@@ -2869,12 +2826,9 @@ void Interface::keyBoss() {
cPal[i].blue = 128;
}
- _vm->_gfx->drawRegion(rect, image);
+ _vm->_gfx->drawRegion(rect, image.getBuffer());
_vm->_gfx->setPalette(cPal);
-
- free(resource);
- free(image);
}
diff --git a/engines/saga/interface.h b/engines/saga/interface.h
index 0fbe5bef20..b9a96653a7 100644
--- a/engines/saga/interface.h
+++ b/engines/saga/interface.h
@@ -94,8 +94,7 @@ enum FadeModes {
struct InterfacePanel {
int x;
int y;
- byte *image;
- size_t imageLength;
+ ByteArray image;
int imageWidth;
int imageHeight;
@@ -106,8 +105,6 @@ struct InterfacePanel {
InterfacePanel() {
x = y = 0;
- image = NULL;
- imageLength = 0;
imageWidth = imageHeight = 0;
currentButton = NULL;
buttonsCount = 0;
@@ -164,7 +161,7 @@ struct InterfacePanel {
};
struct Converse {
- char *text;
+ Common::Array<char> text;
int strId;
int stringNum;
int textNum;
@@ -356,7 +353,6 @@ private:
void processStatusTextInput(Common::KeyState keystate);
public:
- void converseInit();
void converseClear();
bool converseAddText(const char *text, int strId, int replyId, byte replyFlags, int replyBit);
void converseDisplayText();
@@ -431,8 +427,7 @@ private:
Point _lastMousePoint;
- uint16 *_inventory;
- int _inventorySize;
+ Common::Array<uint16> _inventory;
int _inventoryStart;
int _inventoryEnd;
int _inventoryPos;
diff --git a/engines/saga/introproc_ihnm.cpp b/engines/saga/introproc_ihnm.cpp
index e149753dfd..2053c7158f 100644
--- a/engines/saga/introproc_ihnm.cpp
+++ b/engines/saga/introproc_ihnm.cpp
@@ -83,19 +83,18 @@ int Scene::IHNMStartProc() {
}
_vm->_music->setVolume(0, 1000);
- _vm->_anim->freeCutawayList();
+ _vm->_anim->clearCutawayList();
// Queue first scene
firstScene.loadFlag = kLoadBySceneNumber;
firstScene.sceneDescriptor = -1;
- firstScene.sceneDescription = NULL;
firstScene.sceneSkipTarget = false;
firstScene.sceneProc = NULL;
firstScene.transitionType = kTransitionFade;
firstScene.actorsEntrance = 0;
firstScene.chapter = -1;
- _vm->_scene->queueScene(&firstScene);
+ _vm->_scene->queueScene(firstScene);
return SUCCESS;
}
@@ -114,7 +113,7 @@ int Scene::IHNMCreditsProc() {
}
_vm->_music->setVolume(0, 1000);
- _vm->_anim->freeCutawayList();
+ _vm->_anim->clearCutawayList();
return SUCCESS;
}
@@ -122,8 +121,7 @@ int Scene::IHNMCreditsProc() {
void Scene::IHNMLoadCutaways() {
ResourceContext *resourceContext;
//ResourceContext *soundContext;
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
if (resourceContext == NULL) {
@@ -131,18 +129,16 @@ void Scene::IHNMLoadCutaways() {
}
if (!_vm->isIHNMDemo())
- _vm->_resource->loadResource(resourceContext, RID_IHNM_INTRO_CUTAWAYS, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_INTRO_CUTAWAYS, resourceData);
else
- _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_INTRO_CUTAWAYS, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_INTRO_CUTAWAYS, resourceData);
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Scene::IHNMStartProc() Can't load cutaway list");
}
// Load the cutaways for the title screens
- _vm->_anim->loadCutawayList(resourcePointer, resourceLength);
-
- free(resourcePointer);
+ _vm->_anim->loadCutawayList(resourceData);
}
bool Scene::checkKey() {
diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp
index 83fdadf59e..ae7dedefa5 100644
--- a/engines/saga/introproc_ite.cpp
+++ b/engines/saga/introproc_ite.cpp
@@ -61,15 +61,15 @@ using Common::IT_ITA;
#define MUSIC_2 10
LoadSceneParams ITE_IntroList[] = {
- {RID_ITE_INTRO_ANIM_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroAnimProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_1, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave1Proc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_2, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave2Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_3, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave3Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_4, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave4Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_VALLEY_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroValleyProc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_TREEHOUSE_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroTreeHouseProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_FAIREPATH_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroFairePathProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_FAIRETENT_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroFaireTentProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}
+ {RID_ITE_INTRO_ANIM_SCENE, kLoadByResourceId, Scene::SC_ITEIntroAnimProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_1, kLoadByResourceId, Scene::SC_ITEIntroCave1Proc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_2, kLoadByResourceId, Scene::SC_ITEIntroCave2Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_3, kLoadByResourceId, Scene::SC_ITEIntroCave3Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_4, kLoadByResourceId, Scene::SC_ITEIntroCave4Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_VALLEY_SCENE, kLoadByResourceId, Scene::SC_ITEIntroValleyProc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_TREEHOUSE_SCENE, kLoadByResourceId, Scene::SC_ITEIntroTreeHouseProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_FAIREPATH_SCENE, kLoadByResourceId, Scene::SC_ITEIntroFairePathProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_FAIRETENT_SCENE, kLoadByResourceId, Scene::SC_ITEIntroFaireTentProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}
};
int Scene::ITEStartProc() {
@@ -84,25 +84,24 @@ int Scene::ITEStartProc() {
for (i = 0; i < scenesCount; i++) {
tempScene = ITE_IntroList[i];
tempScene.sceneDescriptor = _vm->_resource->convertResourceId(tempScene.sceneDescriptor);
- _vm->_scene->queueScene(&tempScene);
+ _vm->_scene->queueScene(tempScene);
}
firstScene.loadFlag = kLoadBySceneNumber;
firstScene.sceneDescriptor = _vm->getStartSceneNumber();
- firstScene.sceneDescription = NULL;
firstScene.sceneSkipTarget = true;
firstScene.sceneProc = NULL;
firstScene.transitionType = kTransitionFade;
firstScene.actorsEntrance = 0;
firstScene.chapter = -1;
- _vm->_scene->queueScene(&firstScene);
+ _vm->_scene->queueScene(firstScene);
return SUCCESS;
}
-Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialogue dialogue[]) {
+EventColumns *Scene::ITEQueueDialogue(EventColumns *eventColumns, int n_dialogues, const IntroDialogue dialogue[]) {
TextListEntry textEntry;
TextListEntry *entry;
Event event;
@@ -136,7 +135,7 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo
event.op = kEventDisplay;
event.data = entry;
event.time = (i == 0) ? 0 : VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
// Play voice
event.type = kEvTOneshot;
@@ -144,7 +143,7 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo
event.op = kEventPlay;
event.param = dialogue[i].i_voice_rn;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
voice_len = _vm->_sndRes->getVoiceLength(dialogue[i].i_voice_rn);
if (voice_len < 0) {
@@ -157,10 +156,10 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo
event.op = kEventRemove;
event.data = entry;
event.time = voice_len;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
- return q_event;
+ return eventColumns;
}
enum {
@@ -180,7 +179,7 @@ enum {
// Queue a page of credits text. The original interpreter did word-wrapping
// automatically. We currently don't.
-Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]) {
+EventColumns *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]) {
int game;
Common::Language lang;
@@ -241,7 +240,7 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const
TextListEntry textEntry;
TextListEntry *entry;
Event event;
- Event *q_event = NULL;
+ EventColumns *eventColumns = NULL;
textEntry.knownColor = kKnownColorSubtitleTextColor;
textEntry.effectKnownColor = kKnownColorTransparent;
@@ -283,7 +282,7 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const
event.op = kEventDisplay;
event.data = entry;
event.time = delta_time;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Remove text
event.type = kEvTOneshot;
@@ -291,12 +290,12 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const
event.op = kEventRemove;
event.data = entry;
event.time = duration;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
y += (_vm->_font->getHeight(font) + line_spacing);
}
- return q_event;
+ return eventColumns;
}
int Scene::SC_ITEIntroAnimProc(int param, void *refCon) {
@@ -306,7 +305,7 @@ int Scene::SC_ITEIntroAnimProc(int param, void *refCon) {
// Handles the introductory Dreamer's Guild / NWC logo animation scene.
int Scene::ITEIntroAnimProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
switch (param) {
case SCENE_BEGIN:{
@@ -317,7 +316,7 @@ int Scene::ITEIntroAnimProc(int param) {
event.op = kEventDisplay;
event.param = kEvPSetPalette;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
debug(3, "Intro animation procedure started.");
debug(3, "Linking animation resources...");
@@ -355,7 +354,7 @@ int Scene::ITEIntroAnimProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue intro music playback
event.type = kEvTOneshot;
@@ -364,7 +363,7 @@ int Scene::ITEIntroAnimProc(int param) {
event.param2 = MUSIC_LOOP;
event.op = kEventPlay;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
break;
case SCENE_END:
@@ -384,7 +383,7 @@ int Scene::SC_ITEIntroCave1Proc(int param, void *refCon) {
// Handles first introductory cave painting scene
int Scene::ITEIntroCave1Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -468,24 +467,24 @@ int Scene::ITEIntroCave1Proc(int param) {
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -499,7 +498,7 @@ int Scene::SC_ITEIntroCave2Proc(int param, void *refCon) {
// Handles second introductory cave painting scene
int Scene::ITEIntroCave2Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -566,30 +565,30 @@ int Scene::ITEIntroCave2Proc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin palette cycling animation for candles
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -603,7 +602,7 @@ int Scene::SC_ITEIntroCave3Proc(int param, void *refCon) {
// Handles third introductory cave painting scene
int Scene::ITEIntroCave3Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -671,30 +670,30 @@ int Scene::ITEIntroCave3Proc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin palette cycling animation for candles
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -708,7 +707,7 @@ int Scene::SC_ITEIntroCave4Proc(int param, void *refCon) {
// Handles fourth introductory cave painting scene
int Scene::ITEIntroCave4Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -789,30 +788,30 @@ int Scene::ITEIntroCave4Proc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin palette cycling animation for candles
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -826,7 +825,7 @@ int Scene::SC_ITEIntroValleyProc(int param, void *refCon) {
// Handles intro title scene (valley overlook)
int Scene::ITEIntroValleyProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static const IntroCredit credits[] = {
{EN_ANY, kITEAny, kCHeader, "Producer"},
@@ -856,7 +855,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin ITE title theme music
_vm->_music->stop();
@@ -867,7 +866,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.param2 = MUSIC_NORMAL;
event.op = kEventPlay;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Pause animation before logo
event.type = kEvTOneshot;
@@ -875,7 +874,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventStop;
event.param = 0;
event.time = 3000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Display logo
event.type = kEvTContinuous;
@@ -883,7 +882,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventDissolveBGMask;
event.time = 0;
event.duration = LOGO_DISSOLVE_DURATION;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Remove logo
event.type = kEvTContinuous;
@@ -891,7 +890,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventDissolve;
event.time = 3000;
event.duration = LOGO_DISSOLVE_DURATION;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Unpause animation before logo
event.type = kEvTOneshot;
@@ -899,17 +898,17 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventPlay;
event.time = 0;
event.param = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue game credits list
- q_event = ITEQueueCredits(9000, CREDIT_DURATION1, n_credits, credits);
+ eventColumns = ITEQueueCredits(9000, CREDIT_DURATION1, n_credits, credits);
// End scene after credit display
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 1000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
@@ -929,7 +928,7 @@ int Scene::SC_ITEIntroTreeHouseProc(int param, void *refCon) {
// Handles second intro credit screen (treehouse view)
int Scene::ITEIntroTreeHouseProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static const IntroCredit credits1[] = {
{EN_ANY, kITEAny, kCHeader, "Game Design"},
@@ -981,7 +980,7 @@ int Scene::ITEIntroTreeHouseProc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
if (_vm->_anim->hasAnimation(0)) {
// Begin title screen background animation
@@ -992,19 +991,19 @@ int Scene::ITEIntroTreeHouseProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
// Queue game credits list
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
// End scene after credit display
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 1000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
@@ -1024,7 +1023,7 @@ int Scene::SC_ITEIntroFairePathProc(int param, void *refCon) {
// Handles third intro credit screen (path to puzzle tent)
int Scene::ITEIntroFairePathProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static const IntroCredit credits1[] = {
{EN_ANY, kITEAny, kCHeader, "Programming"},
@@ -1063,7 +1062,7 @@ int Scene::ITEIntroFairePathProc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin title screen background animation
_vm->_anim->setCycles(0, -1);
@@ -1073,18 +1072,18 @@ int Scene::ITEIntroFairePathProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue game credits list
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
// End scene after credit display
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 1000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
@@ -1104,8 +1103,7 @@ int Scene::SC_ITEIntroFaireTentProc(int param, void *refCon) {
// Handles fourth intro credit screen (treehouse view)
int Scene::ITEIntroFaireTentProc(int param) {
Event event;
- Event *q_event;
- Event *q_event_start;
+ EventColumns *eventColumns;
switch (param) {
case SCENE_BEGIN:
@@ -1116,14 +1114,14 @@ int Scene::ITEIntroFaireTentProc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event_start = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// End scene after momentary pause
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 5000;
- q_event = _vm->_events->chain(q_event_start, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp
index f0ad9bbd5e..6450af268a 100644
--- a/engines/saga/isomap.cpp
+++ b/engines/saga/isomap.cpp
@@ -85,34 +85,40 @@ static const IsoMap::TilePoint hardDirTable[8] = {
{ 0, 1, 0, SAGA_STRAIGHT_HARD_COST},
};
-IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
- _tileData = NULL;
- _tileDataLength = 0;
+static const int16 directions[8][2] = {
+ { 16, 16},
+ { 16, 0},
+ { 16, -16},
+ { 0, -16},
+ { -16, -16},
+ { -16, 0},
+ { -16, 16},
+ { 0, 16}
+};
- _multiTableData = NULL;
- _multiDataCount = 0;
+IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
_viewScroll.x = (128 - 8) * 16;
_viewScroll.x = (128 - 8) * 16 - 64;
_viewDiff = 1;
-
}
-void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
+void IsoMap::loadImages(const ByteArray &resourceData) {
IsoTileData *tileData;
uint16 i;
size_t offsetDiff;
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("IsoMap::loadImages wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
readS.readUint16(); // skip
i = readS.readUint16();
i = i / SAGA_ISOTILEDATA_LEN;
_tilesTable.resize(i);
-
+ Common::Array<size_t> tempOffsets;
+ tempOffsets.resize(_tilesTable.size());
readS.seek(0);
@@ -120,7 +126,7 @@ void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
tileData = &_tilesTable[i];
tileData->height = readS.readByte();
tileData->attributes = readS.readSByte();
- tileData->offset = readS.readUint16();
+ tempOffsets[i] = readS.readUint16();
tileData->terrainMask = readS.readUint16();
tileData->FGDBGDAttr = readS.readByte();
readS.readByte(); //skip
@@ -128,26 +134,25 @@ void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
offsetDiff = readS.pos();
- _tileDataLength = resourceLength - offsetDiff;
- _tileData = (byte*)malloc(_tileDataLength);
- memcpy(_tileData, resourcePointer + offsetDiff, _tileDataLength);
+ _tileData.resize(resourceData.size() - offsetDiff);
+ memcpy(_tileData.getBuffer(), resourceData.getBuffer() + offsetDiff, _tileData.size());
for (i = 0; i < _tilesTable.size(); i++) {
- _tilesTable[i].offset -= offsetDiff;
+ _tilesTable[i].tilePointer = _tileData.getBuffer() + tempOffsets[i] - offsetDiff;
}
}
-void IsoMap::loadPlatforms(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadPlatforms(const ByteArray &resourceData) {
TilePlatformData *tilePlatformData;
uint16 i, x, y;
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("IsoMap::loadPlatforms wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
- i = resourceLength / SAGA_TILEPLATFORMDATA_LEN;
+ i = resourceData.size() / SAGA_TILEPLATFORMDATA_LEN;
_tilePlatformList.resize(i);
for (i = 0; i < _tilePlatformList.size(); i++) {
@@ -166,14 +171,14 @@ void IsoMap::loadPlatforms(const byte * resourcePointer, size_t resourceLength)
}
-void IsoMap::loadMap(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadMap(const ByteArray &resourceData) {
uint16 x, y;
- if (resourceLength != SAGA_TILEMAP_LEN) {
- error("IsoMap::loadMap wrong resourceLength");
+ if (resourceData.size() != SAGA_TILEMAP_LEN) {
+ error("IsoMap::loadMap wrong resource length %d", resourceData.size());
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
_tileMap.edgeType = readS.readByte();
readS.readByte(); //skip
@@ -185,16 +190,16 @@ void IsoMap::loadMap(const byte * resourcePointer, size_t resourceLength) {
}
-void IsoMap::loadMetaTiles(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadMetaTiles(const ByteArray &resourceData) {
MetaTileData *metaTileData;
uint16 i, j;
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("IsoMap::loadMetaTiles wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
- i = resourceLength / SAGA_METATILEDATA_LEN;
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
+ i = resourceData.size() / SAGA_METATILEDATA_LEN;
_metaTileList.resize(i);
for (i = 0; i < _metaTileList.size(); i++) {
@@ -207,16 +212,16 @@ void IsoMap::loadMetaTiles(const byte * resourcePointer, size_t resourceLength)
}
}
-void IsoMap::loadMulti(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadMulti(const ByteArray &resourceData) {
MultiTileEntryData *multiTileEntryData;
uint16 i;
int16 offsetDiff;
- if (resourceLength < 2) {
+ if (resourceData.size() < 2) {
error("IsoMap::loadMetaTiles wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
i = readS.readUint16();
_multiTable.resize(i);
@@ -240,26 +245,21 @@ void IsoMap::loadMulti(const byte * resourcePointer, size_t resourceLength) {
_multiTable[i].offset -= offsetDiff;
}
- _multiDataCount = (readS.size() - readS.pos()) / 2;
+ uint16 multiDataCount = (readS.size() - readS.pos()) / 2;
- _multiTableData = (int16 *)malloc(_multiDataCount * sizeof(*_multiTableData));
- for (i = 0; i < _multiDataCount; i++) {
+ _multiTableData.resize(multiDataCount);
+ for (i = 0; i < _multiTableData.size(); i++) {
_multiTableData[i] = readS.readSint16();
}
}
-void IsoMap::freeMem() {
+void IsoMap::clear() {
_tilesTable.clear();
_tilePlatformList.clear();
_metaTileList.clear();
_multiTable.clear();
-
- free(_tileData);
- _tileData = NULL;
-
- free(_multiTableData);
- _multiTableData = NULL;
- _multiDataCount = 0;
+ _tileData.clear();
+ _multiTableData.clear();
}
void IsoMap::adjustScroll(bool jump) {
@@ -344,12 +344,12 @@ int16 IsoMap::findMulti(int16 tileIndex, int16 absU, int16 absV, int16 absH) {
state = multiTileEntryData->currentState;
offset = (ru + state * multiTileEntryData->uSize) * multiTileEntryData->vSize + rv;
- offset *= sizeof(*_multiTableData);
+ offset *= sizeof(int16);
offset += multiTileEntryData->offset;
- if (offset + sizeof(*_multiTableData) - 1 >= _multiDataCount * sizeof(*_multiTableData)) {
+ if (offset + sizeof(int16) > _multiTableData.size() * sizeof(int16)) {
error("wrong multiTileEntryData->offset");
}
- tiles = (int16*)((byte*)_multiTableData + offset);
+ tiles = (int16*)((byte*)&_multiTableData.front() + offset);
tileIndex = *tiles;
if (tileIndex >= 256) {
warning("something terrible happened");
@@ -707,7 +707,7 @@ void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *loca
return;
}
- tilePointer = _tileData + _tilesTable[tileIndex].offset;
+ tilePointer = _tilesTable[tileIndex].tilePointer;
height = _tilesTable[tileIndex].height;
if ((height <= 8) || (height > 64)) {
@@ -831,18 +831,30 @@ void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *loca
widthCount += fgRunCount;
count = 0;
- while ((col < _tileClip.left) && (count < fgRunCount)) {
- count++;
- col++;
+ int colDiff = _tileClip.left - col;
+ if (colDiff > 0) {
+ if (colDiff > fgRunCount) {
+ colDiff = fgRunCount;
+ }
+ count = colDiff;
+ col += colDiff;
}
- while ((col < _tileClip.right) && (count < fgRunCount)) {
- assert(_vm->_gfx->getBackBufferPixels() <= (byte *)(drawPointer + count));
- assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width *
- _vm->getDisplayInfo().height)) > (byte *)(drawPointer + count));
- drawPointer[count] = readPointer[count];
- count++;
- col++;
+
+ colDiff = _tileClip.right - col;
+ if (colDiff > 0) {
+ int countDiff = fgRunCount - count;
+ if (colDiff > countDiff) {
+ colDiff = countDiff;
+ }
+ if (colDiff > 0) {
+ byte *dst = (byte *)(drawPointer + count);
+ assert(_vm->_gfx->getBackBufferPixels() <= dst);
+ assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width * _vm->getDisplayInfo().height)) >= (byte *)(dst + colDiff));
+ memcpy(dst, (readPointer + count), colDiff);
+ col += colDiff;
+ }
}
+
readPointer += fgRunCount;
drawPointer += fgRunCount;
}
@@ -1149,8 +1161,6 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista
int16 vBase;
int16 u;
int16 v;
- int i;
- ActorData *actor;
TilePoint tilePoint;
uint16 dir;
int16 dist;
@@ -1171,8 +1181,7 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista
memset( &_searchArray, 0, sizeof(_searchArray));
- for (i = 0; i < _vm->_actor->_actorsCount; i++) {
- actor = _vm->_actor->_actors[i];
+ for (ActorDataArray::const_iterator actor = _vm->_actor->_actors.begin(); actor != _vm->_actor->_actors.end(); ++actor) {
if (!actor->_inScene) continue;
u = (actor->_location.u() >> 4) - uBase;
@@ -1452,14 +1461,13 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo
actor->_walkStepsCount = i;
if (i) {
- actor->setTileDirectionsSize(i, false);
- memcpy(actor->_tileDirections, res, i);
+ actor->_tileDirections.resize(i);
+ memcpy(&actor->_tileDirections.front(), res, i);
}
}
void IsoMap::findTilePath(ActorData* actor, const Location &start, const Location &end) {
- ActorData *other;
int i;
int16 u;
int16 v;
@@ -1499,10 +1507,9 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio
if (!(actor->_actorFlags & kActorNoCollide) &&
(_vm->_scene->currentSceneResourceId() != ITE_SCENE_OVERMAP)) {
- for (i = 0; i < _vm->_actor->_actorsCount; i++) {
- other = _vm->_actor->_actors[i];
+ for (ActorDataArray::const_iterator other = _vm->_actor->_actors.begin(); other != _vm->_actor->_actors.end(); ++other) {
if (!other->_inScene) continue;
- if (other == actor) continue;
+ if (other->_id == actor->_id) continue;
u = (other->_location.u() >> 4) - uBase;
v = (other->_location.v() >> 4) - vBase;
@@ -1585,8 +1592,8 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio
actor->_walkStepsCount = i;
if (i) {
- actor->setTileDirectionsSize(i, false);
- memcpy(actor->_tileDirections, res, i);
+ actor->_tileDirections.resize(i);
+ memcpy(&actor->_tileDirections.front(), res, i);
}
}
@@ -1601,19 +1608,6 @@ void IsoMap::setTileDoorState(int doorNumber, int doorState) {
multiTileEntryData->currentState = doorState;
}
-static const int16 directions[8][2] = {
- { 16, 16},
- { 16, 0},
- { 16, -16},
- { 0, -16},
- { -16, -16},
- { -16, 0},
- { -16, 16},
- { 0, 16}
-};
-
-
-
bool IsoMap::nextTileTarget(ActorData* actor) {
uint16 dir;
diff --git a/engines/saga/isomap.h b/engines/saga/isomap.h
index 46173e2b13..7dc7dff8cd 100644
--- a/engines/saga/isomap.h
+++ b/engines/saga/isomap.h
@@ -95,7 +95,7 @@ enum TileMapEdgeType {
struct IsoTileData {
byte height;
int8 attributes;
- size_t offset;
+ byte *tilePointer;
uint16 terrainMask;
byte FGDBGDAttr;
int8 GetMaskRule() const {
@@ -154,14 +154,13 @@ class IsoMap {
public:
IsoMap(SagaEngine *vm);
~IsoMap() {
- freeMem();
}
- void loadImages(const byte * resourcePointer, size_t resourceLength);
- void loadMap(const byte * resourcePointer, size_t resourceLength);
- void loadPlatforms(const byte * resourcePointer, size_t resourceLength);
- void loadMetaTiles(const byte * resourcePointer, size_t resourceLength);
- void loadMulti(const byte * resourcePointer, size_t resourceLength);
- void freeMem();
+ void loadImages(const ByteArray &resourceData);
+ void loadMap(const ByteArray &resourceData);
+ void loadPlatforms(const ByteArray &resourceData);
+ void loadMetaTiles(const ByteArray &resourceData);
+ void loadMulti(const ByteArray &resourceData);
+ void clear();
void draw();
void drawSprite(SpriteList &spriteList, int spriteNumber, const Location &location, const Point &screenPosition, int scale);
void adjustScroll(bool jump);
@@ -213,16 +212,14 @@ private:
IsoTileData *getTile(int16 u, int16 v, int16 z);
- byte *_tileData;
- size_t _tileDataLength;
+ ByteArray _tileData;
Common::Array<IsoTileData> _tilesTable;
Common::Array<TilePlatformData> _tilePlatformList;
Common::Array<MetaTileData> _metaTileList;
Common::Array<MultiTileEntryData> _multiTable;
- uint16 _multiDataCount;
- int16 *_multiTableData;
+ Common::Array<int16> _multiTableData;
TileMapData _tileMap;
diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp
index 7503818319..ab0aa12d18 100644
--- a/engines/saga/itedata.cpp
+++ b/engines/saga/itedata.cpp
@@ -269,7 +269,7 @@ ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT] = {
{ 54, 281, 620, 352, 0, 80, 46, 0 } // Orb of Storms in Dam Lab
};
-FxTable ITE_SfxTable[ITE_SFXCOUNT] = {
+IteFxTable ITE_SfxTable[ITE_SFXCOUNT] = {
{ 14, 127 }, // Door open
{ 15, 127 }, // Door close
{ 16, 63 }, // Rush water (floppy volume: 127)
diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h
index 71041902bc..f0f626a51a 100644
--- a/engines/saga/itedata.h
+++ b/engines/saga/itedata.h
@@ -77,16 +77,16 @@ struct ObjectTableData {
uint16 interactBits;
};
-struct FxTable {
- int res;
- int vol;
+struct IteFxTable {
+ byte res;
+ byte vol;
};
#define ITE_OBJECTCOUNT 39
#define ITE_SFXCOUNT 63
extern ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT];
-extern FxTable ITE_SfxTable[ITE_SFXCOUNT];
+extern IteFxTable ITE_SfxTable[ITE_SFXCOUNT];
extern const char *ITEinterfaceTextStrings[][53];
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 199b0dfd8a..081fe955fc 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -36,6 +36,7 @@
#include "sound/decoders/raw.h"
#include "common/config-manager.h"
#include "common/file.h"
+#include "common/substream.h"
namespace Saga {
@@ -117,6 +118,7 @@ void MusicDriver::send(uint32 b) {
Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
_currentVolume = 0;
+ _currentMusicBuffer = NULL;
_driver = new MusicDriver();
_digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE);
@@ -162,11 +164,13 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
// Just set an XMIDI parser for Mac IHNM for now
_parser = MidiParser::createParser_XMIDI();
} else {
- byte *resourceData;
- size_t resourceSize;
+ ByteArray resourceData;
int resourceId = (_vm->getGameId() == GID_ITE ? 9 : 0);
- _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
- if (!memcmp(resourceData, "FORM", 4)) {
+ _vm->_resource->loadResource(_musicContext, resourceId, resourceData);
+ if (resourceData.size() < 4) {
+ error("Music::Music Unable to load midi resource data");
+ }
+ if (!memcmp(resourceData.getBuffer(), "FORM", 4)) {
_parser = MidiParser::createParser_XMIDI();
// ITE had MT32 mapped instruments
_driver->setGM(_vm->getGameId() != GID_ITE);
@@ -175,17 +179,12 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
// ITE with standalone MIDI files is General MIDI
_driver->setGM(_vm->getGameId() == GID_ITE);
}
- free(resourceData);
}
-
+
_parser->setMidiDriver(_driver);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
- _songTableLen = 0;
- _songTable = 0;
-
- _midiMusicData = NULL;
_digitalMusic = false;
}
@@ -196,9 +195,6 @@ Music::~Music() {
delete _driver;
_parser->setMidiDriver(NULL);
delete _parser;
-
- free(_songTable);
- free(_midiMusicData);
}
void Music::musicVolumeGaugeCallback(void *refCon) {
@@ -258,9 +254,7 @@ bool Music::isPlaying() {
void Music::play(uint32 resourceId, MusicFlags flags) {
Audio::SeekableAudioStream *audioStream = NULL;
- byte *resourceData;
- size_t resourceSize;
- uint32 loopStart;
+ uint32 loopStart = 0;
debug(2, "Music::play %d, %d", resourceId, flags);
@@ -392,14 +386,19 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
#endif
return;
} else {
- _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
+ if (_currentMusicBuffer == &_musicBuffer[1]) {
+ _currentMusicBuffer = &_musicBuffer[0];
+ } else {
+ _currentMusicBuffer = &_musicBuffer[1];
+ }
+ _vm->_resource->loadResource(_musicContext, resourceId, *_currentMusicBuffer);
}
- if (resourceSize < 4) {
+ if (_currentMusicBuffer->size() < 4) {
error("Music::play() wrong music resource size");
}
- if (!_parser->loadMusic(resourceData, resourceSize))
+ if (!_parser->loadMusic(_currentMusicBuffer->getBuffer(), _currentMusicBuffer->size()))
error("Music::play() wrong music resource");
_parser->setTrack(0);
@@ -409,9 +408,6 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
// Handle music looping
_parser->property(MidiParser::mpAutoLoop, (flags & MUSIC_LOOP) ? 1 : 0);
-
- free(_midiMusicData);
- _midiMusicData = resourceData;
}
void Music::pause() {
diff --git a/engines/saga/music.h b/engines/saga/music.h
index 470b6e18b3..c7fef7225b 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -28,9 +28,9 @@
#ifndef SAGA_MUSIC_H
#define SAGA_MUSIC_H
-#include "sound/mixer.h"
#include "sound/mididrv.h"
#include "sound/midiparser.h"
+#include "sound/mixer.h"
#include "sound/decoders/mp3.h"
#include "sound/decoders/vorbis.h"
#include "sound/decoders/flac.h"
@@ -106,8 +106,7 @@ public:
void setVolume(int volume, int time = 1);
int getVolume() { return _currentVolume; }
- int32 *_songTable;
- int _songTableLen;
+ Common::Array<int32> _songTable;
private:
SagaEngine *_vm;
@@ -126,11 +125,12 @@ private:
ResourceContext *_digitalMusicContext;
MidiParser *_parser;
- byte *_midiMusicData;
static void musicVolumeGaugeCallback(void *refCon);
static void onTimer(void *refCon);
void musicVolumeGauge();
+ ByteArray *_currentMusicBuffer;
+ ByteArray _musicBuffer[2];
};
} // End of namespace Saga
diff --git a/engines/saga/objectmap.cpp b/engines/saga/objectmap.cpp
index 02179d1d49..9799edc34a 100644
--- a/engines/saga/objectmap.cpp
+++ b/engines/saga/objectmap.cpp
@@ -45,84 +45,58 @@
namespace Saga {
-HitZone::HitZone(MemoryReadStreamEndian *readStream, int index, int sceneNumber): _index(index) {
- int i, j;
- HitZone::ClickArea *clickArea;
- Point *point;
-
+void HitZone::load(SagaEngine *vm, Common::MemoryReadStreamEndian *readStream, int index, int sceneNumber) {
+ _index = index;
_flags = readStream->readByte();
- _clickAreasCount = readStream->readByte();
+ _clickAreas.resize(readStream->readByte());
_rightButtonVerb = readStream->readByte();
readStream->readByte(); // pad
_nameIndex = readStream->readUint16();
_scriptNumber = readStream->readUint16();
- _clickAreas = (HitZone::ClickArea *)malloc(_clickAreasCount * sizeof(*_clickAreas));
-
- if (_clickAreas == NULL) {
- memoryError("HitZone::HitZone");
- }
-
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- clickArea->pointsCount = readStream->readUint16LE();
+ for (ClickAreas::iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ i->resize(readStream->readUint16LE());
- assert(clickArea->pointsCount);
+ assert(!i->empty());
- clickArea->points = (Point *)malloc(clickArea->pointsCount * sizeof(*(clickArea->points)));
- if (clickArea->points == NULL) {
- memoryError("HitZone::HitZone");
- }
-
- for (j = 0; j < clickArea->pointsCount; j++) {
- point = &clickArea->points[j];
- point->x = readStream->readSint16();
- point->y = readStream->readSint16();
+ for (ClickArea::iterator j = i->begin(); j != i->end(); ++j) {
+ j->x = readStream->readSint16();
+ j->y = readStream->readSint16();
// WORKAROUND: bug #1259608: "ITE: Riff ignores command in Ferret merchant center"
// Apparently ITE Mac version has bug in game data. Both ObjectMap and ActionMap
// for exit area are little taller (y = 123) and thus Riff goes to exit
// when clicked on barrel of nails.
- if (sceneNumber == 18 && index == 0 && i == 0 && j == 0 && point->y == 123)
- point->y = 129;
+ if (vm->getGameId() == GID_ITE) {
+ if (sceneNumber == 18 && index == 0 && (i == _clickAreas.begin()) && (j == i->begin()) && j->y == 123) {
+ j->y = 129;
+ }
+ }
}
}
}
-HitZone::~HitZone() {
- for (int i = 0; i < _clickAreasCount; i++) {
- free(_clickAreas[i].points);
- }
- free(_clickAreas);
-}
-
bool HitZone::getSpecialPoint(Point &specialPoint) const {
- int i, pointsCount;
- HitZone::ClickArea *clickArea;
- Point *points;
-
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- pointsCount = clickArea->pointsCount;
- points = clickArea->points;
- if (pointsCount == 1) {
- specialPoint = points[0];
+ for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ if (i->size() == 1) {
+ specialPoint = (*i)[0];
return true;
}
}
return false;
}
+
bool HitZone::hitTest(const Point &testPoint) {
- int i, pointsCount;
- HitZone::ClickArea *clickArea;
- Point *points;
+ const Point *points;
+ uint pointsCount;
if (_flags & kHitZoneEnabled) {
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- pointsCount = clickArea->pointsCount;
- points = clickArea->points;
-
+ for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ pointsCount = i->size();
+ if (pointsCount < 2) {
+ continue;
+ }
+ points = &i->front();
if (pointsCount == 2) {
// Hit-test a box region
if ((testPoint.x >= points[0].x) &&
@@ -132,11 +106,9 @@ bool HitZone::hitTest(const Point &testPoint) {
return true;
}
} else {
- if (pointsCount > 2) {
- // Hit-test a polygon
- if (hitTestPoly(points, pointsCount, testPoint)) {
- return true;
- }
+ // Hit-test a polygon
+ if (hitTestPoly(points, pointsCount, testPoint)) {
+ return true;
}
}
}
@@ -146,26 +118,25 @@ bool HitZone::hitTest(const Point &testPoint) {
#ifdef SAGA_DEBUG
void HitZone::draw(SagaEngine *vm, int color) {
- int i, pointsCount, j;
+ int pointsCount, j;
Location location;
- HitZone::ClickArea *clickArea;
- Point *points;
+ HitZone::ClickArea tmpPoints;
+ const Point *points;
Point specialPoint1;
Point specialPoint2;
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- pointsCount = clickArea->pointsCount;
+ for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ pointsCount = i->size();
+ points = &i->front();
if (vm->_scene->getFlags() & kSceneFlagISO) {
- points = (Point*)malloc(sizeof(Point) * pointsCount);
+ tmpPoints.resize(pointsCount);
for (j = 0; j < pointsCount; j++) {
- location.u() = clickArea->points[j].x;
- location.v() = clickArea->points[j].y;
+ location.u() = points[j].x;
+ location.v() = points[j].y;
location.z = 0;
- vm->_isoMap->tileCoordsToScreenPoint(location, points[j]);
+ vm->_isoMap->tileCoordsToScreenPoint(location, tmpPoints[j]);
}
- } else {
- points = clickArea->points;
+ points = &tmpPoints.front();
}
if (pointsCount == 2) {
@@ -179,10 +150,6 @@ void HitZone::draw(SagaEngine *vm, int color) {
vm->_gfx->drawPolyLine(points, pointsCount, color);
}
}
- if (vm->_scene->getFlags() & kSceneFlagISO) {
- free(points);
- }
-
}
if (getSpecialPoint(specialPoint1)) {
specialPoint2 = specialPoint1;
@@ -196,55 +163,36 @@ void HitZone::draw(SagaEngine *vm, int color) {
#endif
// Loads an object map resource ( objects ( clickareas ( points ) ) )
-void ObjectMap::load(const byte *resourcePointer, size_t resourceLength) {
- int i;
+void ObjectMap::load(const ByteArray &resourceData) {
- if (resourceLength == 0) {
- return;
+ if (!_hitZoneList.empty()) {
+ error("ObjectMap::load _hitZoneList not empty");
}
- if (resourceLength < 4) {
- error("ObjectMap::load wrong resourceLength");
+ if (resourceData.empty()) {
+ return;
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
-
- _hitZoneListCount = readS.readSint16();
- if (_hitZoneListCount < 0) {
- error("ObjectMap::load _hitZoneListCount < 0");
+ if (resourceData.size() < 4) {
+ error("ObjectMap::load wrong resourceLength");
}
- if (_hitZoneList)
- error("ObjectMap::load _hitZoneList != NULL");
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
- _hitZoneList = (HitZone **) malloc(_hitZoneListCount * sizeof(HitZone *));
- if (_hitZoneList == NULL) {
- memoryError("ObjectMap::load");
- }
+ _hitZoneList.resize(readS.readUint16());
- for (i = 0; i < _hitZoneListCount; i++) {
- _hitZoneList[i] = new HitZone(&readS, i, _vm->_scene->currentSceneNumber());
+ int idx = 0;
+ for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
+ i->load(_vm, &readS, idx++, _vm->_scene->currentSceneNumber());
}
}
-void ObjectMap::freeMem() {
- int i;
-
- if (_hitZoneList) {
- for (i = 0; i < _hitZoneListCount; i++) {
- delete _hitZoneList[i];
- }
-
- free(_hitZoneList);
- _hitZoneList = NULL;
- }
- _hitZoneListCount = 0;
+void ObjectMap::clear() {
+ _hitZoneList.clear();
}
-
#ifdef SAGA_DEBUG
void ObjectMap::draw(const Point& testPoint, int color, int color2) {
- int i;
int hitZoneIndex;
char txtBuf[32];
Point pickPoint;
@@ -260,8 +208,8 @@ void ObjectMap::draw(const Point& testPoint, int color, int color2) {
hitZoneIndex = hitTest(pickPoint);
- for (i = 0; i < _hitZoneListCount; i++) {
- _hitZoneList[i]->draw(_vm, (hitZoneIndex == i) ? color2 : color);
+ for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
+ i->draw(_vm, (hitZoneIndex == i->getIndex()) ? color2 : color);
}
if (hitZoneIndex != -1) {
@@ -274,12 +222,11 @@ void ObjectMap::draw(const Point& testPoint, int color, int color2) {
#endif
int ObjectMap::hitTest(const Point& testPoint) {
- int i;
// Loop through all scene objects
- for (i = 0; i < _hitZoneListCount; i++) {
- if (_hitZoneList[i]->hitTest(testPoint)) {
- return i;
+ for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
+ if (i->hitTest(testPoint)) {
+ return i->getIndex();
}
}
@@ -287,7 +234,7 @@ int ObjectMap::hitTest(const Point& testPoint) {
}
void ObjectMap::cmdInfo() {
- _vm->_console->DebugPrintf("%d zone(s) loaded.\n\n", _hitZoneListCount);
+ _vm->_console->DebugPrintf("%d zone(s) loaded.\n\n", _hitZoneList.size());
}
} // End of namespace Saga
diff --git a/engines/saga/objectmap.h b/engines/saga/objectmap.h
index df0dcffe57..9e7c004fc0 100644
--- a/engines/saga/objectmap.h
+++ b/engines/saga/objectmap.h
@@ -33,15 +33,14 @@ namespace Saga {
class HitZone {
private:
- struct ClickArea {
- int pointsCount;
- Point *points;
- };
-
+ typedef Common::Array<Point> ClickArea;
+ typedef Common::Array<ClickArea> ClickAreas;
public:
- HitZone(MemoryReadStreamEndian *readStream, int index, int sceneNumber);
- ~HitZone();
+ void load(SagaEngine *vm, Common::MemoryReadStreamEndian *readStream, int index, int sceneNumber);
+ int getIndex() const {
+ return _index;
+ }
int getNameIndex() const {
return _nameIndex;
}
@@ -76,40 +75,38 @@ public:
return objectIndexToId(kGameObjectStepZone, _index);
}
bool getSpecialPoint(Point &specialPoint) const;
+#ifdef SAGA_DEBUG
void draw(SagaEngine *vm, int color); // for debugging
+#endif
bool hitTest(const Point &testPoint);
private:
int _flags; // Saga::HitZoneFlags
- int _clickAreasCount;
int _rightButtonVerb;
int _nameIndex;
int _scriptNumber;
int _index;
- ClickArea *_clickAreas;
+ ClickAreas _clickAreas;
};
+typedef Common::Array<HitZone> HitZoneArray;
class ObjectMap {
public:
ObjectMap(SagaEngine *vm) : _vm(vm) {
- _hitZoneList = NULL;
- _hitZoneListCount = 0;
-
- }
- ~ObjectMap() {
- freeMem();
}
- void load(const byte *resourcePointer, size_t resourceLength);
- void freeMem();
+ void load(const ByteArray &resourceData);
+ void clear();
+#ifdef SAGA_DEBUG
void draw(const Point& testPoint, int color, int color2); // for debugging
+#endif
int hitTest(const Point& testPoint);
HitZone *getHitZone(int16 index) {
- if ((index < 0) || (index >= _hitZoneListCount)) {
+ if (uint(index) >= _hitZoneList.size()) {
return NULL;
}
- return _hitZoneList[index];
+ return &_hitZoneList[index];
}
void cmdInfo();
@@ -117,8 +114,7 @@ public:
private:
SagaEngine *_vm;
- int _hitZoneListCount;
- HitZone **_hitZoneList;
+ HitZoneArray _hitZoneList;
};
} // End of namespace Saga
diff --git a/engines/saga/palanim.cpp b/engines/saga/palanim.cpp
index dc892b845a..b0b76fc947 100644
--- a/engines/saga/palanim.cpp
+++ b/engines/saga/palanim.cpp
@@ -35,126 +35,95 @@
namespace Saga {
PalAnim::PalAnim(SagaEngine *vm) : _vm(vm) {
- _loaded = false;
- _entryCount = 0;
- _entries = NULL;
}
-PalAnim::~PalAnim() {
-}
-
-int PalAnim::loadPalAnim(const byte *resdata, size_t resdata_len) {
- void *test_p;
+void PalAnim::loadPalAnim(const ByteArray &resourceData) {
- uint16 i;
+ clear();
- if (_loaded) {
- freePalAnim();
+ if (resourceData.empty()) {
+ return;
}
- if (resdata == NULL) {
- return FAILURE;
- }
-
- MemoryReadStreamEndian readS(resdata, resdata_len, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
if (_vm->getGameId() == GID_IHNM) {
- return SUCCESS;
+ return;
}
- _entryCount = readS.readUint16();
-
- debug(3, "PalAnim::loadPalAnim(): Loading %d PALANIM entries.", _entryCount);
+ _entries.resize(readS.readUint16());
- test_p = malloc(_entryCount * sizeof(PalanimEntry));
- _entries = (PalanimEntry *)test_p;
+ debug(3, "PalAnim::loadPalAnim(): Loading %d PALANIM entries.", _entries.size());
- for (i = 0; i < _entryCount; i++) {
- int color_count;
- int pal_count;
- int p, c;
+ for (Common::Array<PalanimEntry>::iterator i = _entries.begin(); i != _entries.end(); ++i) {
+
+ i->cycle = 0;
- _entries[i].cycle = 0;
+ i->colors.resize(readS.readUint16());
+ debug(2, "PalAnim::loadPalAnim(): Loading %d SAGA_COLOR structures.", i->colors.size());
- color_count = readS.readUint16();
- pal_count = readS.readUint16();
+ i->palIndex.resize(readS.readUint16());
+ debug(2, "PalAnim::loadPalAnim(): Loading %d palette indices.\n", i->palIndex.size());
- _entries[i].pal_count = pal_count;
- _entries[i].color_count = color_count;
- debug(2, "PalAnim::loadPalAnim(): Entry %d: Loading %d palette indices.\n", i, pal_count);
-
- test_p = malloc(sizeof(char) * pal_count);
- _entries[i].pal_index = (byte *)test_p;
-
- debug(2, "PalAnim::loadPalAnim(): Entry %d: Loading %d SAGA_COLOR structures.", i, color_count);
-
- test_p = malloc(sizeof(Color) * color_count);
- _entries[i].colors = (Color *)test_p;
-
- for (p = 0; p < pal_count; p++) {
- _entries[i].pal_index[p] = readS.readByte();
+ for (uint j = 0; j < i->palIndex.size(); j++) {
+ i->palIndex[j] = readS.readByte();
}
- for (c = 0; c < color_count; c++) {
- _entries[i].colors[c].red = readS.readByte();
- _entries[i].colors[c].green = readS.readByte();
- _entries[i].colors[c].blue = readS.readByte();
+ for (Common::Array<Color>::iterator j = i->colors.begin(); j != i->colors.end(); ++j) {
+ j->red = readS.readByte();
+ j->green = readS.readByte();
+ j->blue = readS.readByte();
}
}
-
- _loaded = true;
- return SUCCESS;
}
-int PalAnim::cycleStart() {
+void PalAnim::cycleStart() {
Event event;
- if (!_loaded) {
- return FAILURE;
+ if (_entries.empty()) {
+ return;
}
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStep;
event.time = PALANIM_CYCLETIME;
- _vm->_events->queue(&event);
-
- return SUCCESS;
+ _vm->_events->queue(event);
}
-int PalAnim::cycleStep(int vectortime) {
+void PalAnim::cycleStep(int vectortime) {
static PalEntry pal[256];
- uint16 pal_index;
- uint16 col_index;
+ uint16 palIndex;
+ uint16 colIndex;
- uint16 i, j;
+ uint16 j;
uint16 cycle;
- uint16 cycle_limit;
+ uint16 cycleLimit;
Event event;
- if (!_loaded) {
- return FAILURE;
+ if (_entries.empty()) {
+ return;
}
_vm->_gfx->getCurrentPal(pal);
- for (i = 0; i < _entryCount; i++) {
- cycle = _entries[i].cycle;
- cycle_limit = _entries[i].color_count;
- for (j = 0; j < _entries[i].pal_count; j++) {
- pal_index = (unsigned char)_entries[i].pal_index[j];
- col_index = (cycle + j) % cycle_limit;
- pal[pal_index].red = (byte) _entries[i].colors[col_index].red;
- pal[pal_index].green = (byte) _entries[i].colors[col_index].green;
- pal[pal_index].blue = (byte) _entries[i].colors[col_index].blue;
+ for (Common::Array<PalanimEntry>::iterator i = _entries.begin(); i != _entries.end(); ++i) {
+ cycle = i->cycle;
+ cycleLimit = i->colors.size();
+ for (j = 0; j < i->palIndex.size(); j++) {
+ palIndex = i->palIndex[j];
+ colIndex = (cycle + j) % cycleLimit;
+ pal[palIndex].red = (byte) i->colors[colIndex].red;
+ pal[palIndex].green = (byte) i->colors[colIndex].green;
+ pal[palIndex].blue = (byte) i->colors[colIndex].blue;
}
- _entries[i].cycle++;
+ i->cycle++;
- if (_entries[i].cycle == cycle_limit) {
- _entries[i].cycle = 0;
+ if (i->cycle == cycleLimit) {
+ i->cycle = 0;
}
}
@@ -167,32 +136,14 @@ int PalAnim::cycleStep(int vectortime) {
event.code = kPalAnimEvent;
event.op = kEventCycleStep;
event.time = vectortime + PALANIM_CYCLETIME;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
- return SUCCESS;
}
-int PalAnim::freePalAnim() {
- uint16 i;
-
- if (!_loaded) {
- return FAILURE;
- }
-
- for (i = 0; i < _entryCount; i++) {
- debug(2, "PalAnim::freePalAnim(): Entry %d: Freeing colors.", i);
- free(_entries[i].colors);
- debug(2, "PalAnim::freePalAnim(): Entry %d: Freeing indices.", i);
- free(_entries[i].pal_index);
- }
-
- debug(3, "PalAnim::freePalAnim(): Freeing entries.");
-
- free(_entries);
-
- _loaded = false;
-
- return SUCCESS;
+void PalAnim::clear() {
+ debug(3, "PalAnim::clear()");
+
+ _entries.clear();
}
} // End of namespace Saga
diff --git a/engines/saga/palanim.h b/engines/saga/palanim.h
index 52002e01c3..2d2c3f1399 100644
--- a/engines/saga/palanim.h
+++ b/engines/saga/palanim.h
@@ -33,29 +33,24 @@ namespace Saga {
#define PALANIM_CYCLETIME 100
struct PalanimEntry {
- uint16 pal_count;
- uint16 color_count;
uint16 cycle;
- byte *pal_index;
- Color *colors;
+ ByteArray palIndex;
+ Common::Array<Color> colors;
};
class PalAnim {
public:
PalAnim(SagaEngine *vm);
- ~PalAnim();
- int loadPalAnim(const byte *, size_t);
- int cycleStart();
- int cycleStep(int vectortime);
- int freePalAnim();
+ void loadPalAnim(const ByteArray &resourceData);
+ void cycleStart();
+ void cycleStep(int vectortime);
+ void clear();
private:
SagaEngine *_vm;
- bool _loaded;
- uint16 _entryCount;
- PalanimEntry *_entries;
+ Common::Array<PalanimEntry> _entries;
};
} // End of namespace Saga
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
index 73839eb6ea..af81c3c670 100644
--- a/engines/saga/puzzle.cpp
+++ b/engines/saga/puzzle.cpp
@@ -172,7 +172,7 @@ void Puzzle::initPieces() {
_vm->_actor->getSpriteParams(puzzle, frameNumber, spriteList);
for (int i = 0; i < PUZZLE_PIECES; i++) {
- spI = &(spriteList->infoList[i]);
+ spI = &((*spriteList)[i]);
_pieceInfo[i].offX = (byte)(spI->width >> 1);
_pieceInfo[i].offY = (byte)(spI->height >> 1);
@@ -347,7 +347,7 @@ void Puzzle::dropPiece(Point mousePt) {
if (newy < boxy)
newy = PUZZLE_Y_OFFSET;
- spI = &(spriteList->infoList[_puzzlePiece]);
+ spI = &((*spriteList)[_puzzlePiece]);
if (newx + spI->width > boxw)
newx = boxw - spI->width ;
diff --git a/engines/saga/resource.cpp b/engines/saga/resource.cpp
index 7d82aa4bda..169c901950 100644
--- a/engines/saga/resource.cpp
+++ b/engines/saga/resource.cpp
@@ -43,8 +43,7 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
size_t i;
bool result;
byte tableInfo[RSC_TABLEINFO_SIZE];
- byte *tableBuffer;
- size_t tableSize;
+ ByteArray tableBuffer;
uint32 count;
uint32 resourceTableOffset;
ResourceData *resourceData;
@@ -59,7 +58,7 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
return false;
}
- MemoryReadStreamEndian readS(tableInfo, RSC_TABLEINFO_SIZE, _isBigEndian);
+ Common::MemoryReadStreamEndian readS(tableInfo, RSC_TABLEINFO_SIZE, _isBigEndian);
resourceTableOffset = readS.readUint32();
count = readS.readUint32();
@@ -70,17 +69,15 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
}
// Load resource table
- tableSize = RSC_TABLEENTRY_SIZE * count;
-
- tableBuffer = (byte *)malloc(tableSize);
+ tableBuffer.resize(RSC_TABLEENTRY_SIZE * count);
_file.seek(resourceTableOffset + contextOffset, SEEK_SET);
- result = (_file.read(tableBuffer, tableSize) == tableSize);
+ result = (_file.read(tableBuffer.getBuffer(), tableBuffer.size()) == tableBuffer.size());
if (result) {
_table.resize(count);
- MemoryReadStreamEndian readS1(tableBuffer, tableSize, _isBigEndian);
+ Common::MemoryReadStreamEndian readS1(tableBuffer.getBuffer(), tableBuffer.size(), _isBigEndian);
for (i = 0; i < count; i++) {
resourceData = &_table[i];
@@ -94,7 +91,6 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
}
}
- free(tableBuffer);
return result;
}
@@ -107,8 +103,6 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
uint32 subjectResourceId;
uint32 patchResourceId;
ResourceData *subjectResourceData;
- byte *tableBuffer;
- size_t tableSize;
bool isMacBinary;
if (_fileName == NULL) { // IHNM special case
@@ -145,10 +139,12 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
if (subjectContext == NULL) {
error("ResourceContext::load() Subject context not found");
}
- resource->loadResource(this, _table.size() - 1, tableBuffer, tableSize);
+ ByteArray tableBuffer;
+
+ resource->loadResource(this, _table.size() - 1, tableBuffer);
- MemoryReadStreamEndian readS2(tableBuffer, tableSize, _isBigEndian);
- for (i = 0; i < tableSize / 8; i++) {
+ ByteArrayReadStreamEndian readS2(tableBuffer, _isBigEndian);
+ for (i = 0; i < tableBuffer.size() / 8; i++) {
subjectResourceId = readS2.readUint32();
patchResourceId = readS2.readUint32();
subjectResourceData = subjectContext->getResourceData(subjectResourceId);
@@ -157,7 +153,6 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
subjectResourceData->offset = resourceData->offset;
subjectResourceData->size = resourceData->size;
}
- free(tableBuffer);
}
//process external patch files
@@ -370,25 +365,25 @@ void Resource::clearContexts() {
}
}
-void Resource::loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize) {
+void Resource::loadResource(ResourceContext *context, uint32 resourceId, ByteArray &resourceBuffer) {
Common::File *file;
uint32 resourceOffset;
ResourceData *resourceData;
- debug(8, "loadResource %d", resourceId);
resourceData = context->getResourceData(resourceId);
file = context->getFile(resourceData);
resourceOffset = resourceData->offset;
- resourceSize = resourceData->size;
- resourceBuffer = (byte*)malloc(resourceSize);
+ debug(8, "loadResource %d 0x%X:0x%X", resourceId, resourceOffset, uint(resourceData->size));
+ resourceBuffer.resize(resourceData->size);
+
file->seek((long)resourceOffset, SEEK_SET);
- if (file->read(resourceBuffer, resourceSize) != resourceSize) {
+ if (file->read(resourceBuffer.getBuffer(), resourceBuffer.size()) != resourceBuffer.size()) {
error("Resource::loadResource() failed to read");
}
diff --git a/engines/saga/resource.h b/engines/saga/resource.h
index e32d16c469..5009c862f4 100644
--- a/engines/saga/resource.h
+++ b/engines/saga/resource.h
@@ -206,7 +206,7 @@ public:
virtual ~Resource();
bool createContexts();
void clearContexts();
- void loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize);
+ void loadResource(ResourceContext *context, uint32 resourceId, ByteArray &resourceBuffer);
virtual uint32 convertResourceId(uint32 resourceId) = 0;
virtual void loadGlobalResources(int chapter, int actorsEntrance) = 0;
diff --git a/engines/saga/resource_res.cpp b/engines/saga/resource_res.cpp
index 8546030241..646de8667b 100644
--- a/engines/saga/resource_res.cpp
+++ b/engines/saga/resource_res.cpp
@@ -48,13 +48,13 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
if (chapter < 0)
chapter = !_vm->isIHNMDemo() ? 8 : 7;
- _vm->_script->_globalVoiceLUT.freeMem();
+ _vm->_script->_globalVoiceLUT.clear();
// TODO: close chapter context, or rather reassign it in our case
ResourceContext *resourceContext;
ResourceContext *soundContext;
- int i;
+ uint i;
resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
if (resourceContext == NULL) {
@@ -66,41 +66,38 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
error("Resource::loadGlobalResources() sound context not found");
}
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
if (!_vm->isIHNMDemo()) {
- _vm->_resource->loadResource(resourceContext, metaResourceTable[chapter],
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, metaResourceTable[chapter], resourceData);
} else {
- _vm->_resource->loadResource(resourceContext, metaResourceTableDemo[chapter],
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, metaResourceTableDemo[chapter], resourceData);
}
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources wrong metaResource");
}
- MemoryReadStream metaS(resourcePointer, resourceLength);
-
- _metaResource.sceneIndex = metaS.readSint16LE();
- _metaResource.objectCount = metaS.readSint16LE();
- _metaResource.objectsStringsResourceID = metaS.readSint32LE();
- _metaResource.inventorySpritesID = metaS.readSint32LE();
- _metaResource.mainSpritesID = metaS.readSint32LE();
- _metaResource.objectsResourceID = metaS.readSint32LE();
- _metaResource.actorCount = metaS.readSint16LE();
- _metaResource.actorsStringsResourceID = metaS.readSint32LE();
- _metaResource.actorsResourceID = metaS.readSint32LE();
- _metaResource.protagFaceSpritesID = metaS.readSint32LE();
- _metaResource.field_22 = metaS.readSint32LE();
- _metaResource.field_26 = metaS.readSint16LE();
- _metaResource.protagStatesCount = metaS.readSint16LE();
- _metaResource.protagStatesResourceID = metaS.readSint32LE();
- _metaResource.cutawayListResourceID = metaS.readSint32LE();
- _metaResource.songTableID = metaS.readSint32LE();
-
- free(resourcePointer);
+ {
+ ByteArrayReadStreamEndian metaS(resourceData);
+
+ _metaResource.sceneIndex = metaS.readSint16LE();
+ _metaResource.objectCount = metaS.readSint16LE();
+ _metaResource.objectsStringsResourceID = metaS.readSint32LE();
+ _metaResource.inventorySpritesID = metaS.readSint32LE();
+ _metaResource.mainSpritesID = metaS.readSint32LE();
+ _metaResource.objectsResourceID = metaS.readSint32LE();
+ _metaResource.actorCount = metaS.readSint16LE();
+ _metaResource.actorsStringsResourceID = metaS.readSint32LE();
+ _metaResource.actorsResourceID = metaS.readSint32LE();
+ _metaResource.protagFaceSpritesID = metaS.readSint32LE();
+ _metaResource.field_22 = metaS.readSint32LE();
+ _metaResource.field_26 = metaS.readSint16LE();
+ _metaResource.protagStatesCount = metaS.readSint16LE();
+ _metaResource.protagStatesResourceID = metaS.readSint32LE();
+ _metaResource.cutawayListResourceID = metaS.readSint32LE();
+ _metaResource.songTableID = metaS.readSint32LE();
+ }
_vm->_actor->loadActorList(actorsEntrance, _metaResource.actorCount,
_metaResource.actorsResourceID, _metaResource.protagStatesCount,
@@ -108,90 +105,83 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
_vm->_actor->_protagonist->_sceneNumber = _metaResource.sceneIndex;
- _vm->_actor->_objectsStrings.freeMem();
+ _vm->_actor->_objectsStrings.clear();
- _vm->_resource->loadResource(resourceContext, _metaResource.objectsStringsResourceID, resourcePointer, resourceLength);
- _vm->loadStrings(_vm->_actor->_objectsStrings, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->_resource->loadResource(resourceContext, _metaResource.objectsStringsResourceID, resourceData);
+ _vm->loadStrings(_vm->_actor->_objectsStrings, resourceData);
- if (chapter >= _vm->_sndRes->_fxTableIDsLen) {
+ if (uint(chapter) >= _vm->_sndRes->_fxTableIDs.size()) {
error("Chapter ID exceeds fxTableIDs length");
}
debug(0, "Going to read %d of %d", chapter, _vm->_sndRes->_fxTableIDs[chapter]);
_vm->_resource->loadResource(soundContext, _vm->_sndRes->_fxTableIDs[chapter],
- resourcePointer, resourceLength);
+ resourceData);
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources Can't load sound effects for current track");
}
- free(_vm->_sndRes->_fxTable);
-
- _vm->_sndRes->_fxTableLen = resourceLength / 4;
- _vm->_sndRes->_fxTable = (FxTable *)malloc(sizeof(FxTable) * _vm->_sndRes->_fxTableLen);
+ _vm->_sndRes->_fxTable.resize(resourceData.size() / 4);
- MemoryReadStream fxS(resourcePointer, resourceLength);
+ {
+ ByteArrayReadStreamEndian fxS(resourceData);
- for (i = 0; i < _vm->_sndRes->_fxTableLen; i++) {
- _vm->_sndRes->_fxTable[i].res = fxS.readSint16LE();
- _vm->_sndRes->_fxTable[i].vol = fxS.readSint16LE();
+ for (i = 0; i < _vm->_sndRes->_fxTable.size(); i++) {
+ _vm->_sndRes->_fxTable[i].res = fxS.readSint16LE();
+ _vm->_sndRes->_fxTable[i].vol = fxS.readSint16LE();
+ }
}
- free(resourcePointer);
- _vm->_interface->_defPortraits.freeMem();
+ _vm->_interface->_defPortraits.clear();
_vm->_sprite->loadList(_metaResource.protagFaceSpritesID, _vm->_interface->_defPortraits);
- _vm->_actor->_actorsStrings.freeMem();
+ _vm->_actor->_actorsStrings.clear();
- _vm->_resource->loadResource(resourceContext, _metaResource.actorsStringsResourceID, resourcePointer, resourceLength);
- _vm->loadStrings(_vm->_actor->_actorsStrings, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->_resource->loadResource(resourceContext, _metaResource.actorsStringsResourceID, resourceData);
+ _vm->loadStrings(_vm->_actor->_actorsStrings, resourceData);
- _vm->_sprite->_inventorySprites.freeMem();
+ _vm->_sprite->_inventorySprites.clear();
_vm->_sprite->loadList(_metaResource.inventorySpritesID, _vm->_sprite->_inventorySprites);
- _vm->_sprite->_mainSprites.freeMem();
+ _vm->_sprite->_mainSprites.clear();
_vm->_sprite->loadList(_metaResource.mainSpritesID, _vm->_sprite->_mainSprites);
_vm->_actor->loadObjList(_metaResource.objectCount, _metaResource.objectsResourceID);
- _vm->_resource->loadResource(resourceContext, _metaResource.cutawayListResourceID, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, _metaResource.cutawayListResourceID, resourceData);
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources Can't load cutaway list");
}
- _vm->_anim->loadCutawayList(resourcePointer, resourceLength);
+ _vm->_anim->loadCutawayList(resourceData);
if (_metaResource.songTableID > 0) {
- _vm->_resource->loadResource(resourceContext, _metaResource.songTableID, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, _metaResource.songTableID, resourceData);
if (chapter == 6) {
- int32 id = READ_LE_UINT32(&resourcePointer[actorsEntrance * 4]);
- free(resourcePointer);
- _vm->_resource->loadResource(resourceContext, id, resourcePointer, resourceLength);
+ if (resourceData.size() < (uint(actorsEntrance) * 4 + 4)) {
+ error("Resource::loadGlobalResources chapter 6 has wrong resource");
+ }
+ int32 id = READ_LE_UINT32(&resourceData[actorsEntrance * 4]);
+ _vm->_resource->loadResource(resourceContext, id, resourceData);
}
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources Can't load songs list for current track");
}
- free(_vm->_music->_songTable);
-
- _vm->_music->_songTableLen = resourceLength / 4;
- _vm->_music->_songTable = (int32 *)malloc(sizeof(int32) * _vm->_music->_songTableLen);
+ _vm->_music->_songTable.resize(resourceData.size() / 4);
- MemoryReadStream songS(resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian songS(resourceData);
- for (i = 0; i < _vm->_music->_songTableLen; i++)
+ for (i = 0; i < _vm->_music->_songTable.size(); i++)
_vm->_music->_songTable[i] = songS.readSint32LE();
- free(resourcePointer);
} else {
// The IHNM demo has a fixed music track and doesn't load a song table
_vm->_music->setVolume(_vm->_musicVolume, 1);
_vm->_music->play(3, MUSIC_LOOP);
- free(resourcePointer);
}
int voiceLUTResourceID = 0;
@@ -207,9 +197,8 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
}
if (voiceLUTResourceID) {
- _vm->_resource->loadResource(resourceContext, voiceLUTResourceID, resourcePointer, resourceLength);
- _vm->_script->loadVoiceLUT(_vm->_script->_globalVoiceLUT, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->_resource->loadResource(resourceContext, voiceLUTResourceID, resourceData);
+ _vm->_script->loadVoiceLUT(_vm->_script->_globalVoiceLUT, resourceData);
}
_vm->_spiritualBarometer = 0;
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 1b7fa97f8d..abd681ce87 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -291,7 +291,7 @@ Common::Error SagaEngine::run() {
_sound = new Sound(this, _mixer);
if (!isSaga2()) {
- _interface->converseInit();
+ _interface->converseClear();
_script->setVerb(_script->getVerbType(kVerbWalkTo));
}
@@ -393,28 +393,26 @@ Common::Error SagaEngine::run() {
return Common::kNoError;
}
-void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength) {
+void SagaEngine::loadStrings(StringsTable &stringsTable, const ByteArray &stringsData) {
uint16 stringsCount;
size_t offset;
size_t prevOffset = 0;
- int i;
+ Common::Array<size_t> tempOffsets;
+ uint ui;
- if (stringsLength == 0) {
+ if (stringsData.empty()) {
error("SagaEngine::loadStrings() Error loading strings list resource");
}
- stringsTable.stringsPointer = (byte*)malloc(stringsLength);
- memcpy(stringsTable.stringsPointer, stringsPointer, stringsLength);
-
- MemoryReadStreamEndian scriptS(stringsTable.stringsPointer, stringsLength, isBigEndian()); //TODO: get endianess from context
+ ByteArrayReadStreamEndian scriptS(stringsData, isBigEndian()); //TODO: get endianess from context
offset = scriptS.readUint16();
stringsCount = offset / 2;
- stringsTable.strings = (const char **)malloc(stringsCount * sizeof(*stringsTable.strings));
- i = 0;
+ ui = 0;
scriptS.seek(0);
- while (i < stringsCount) {
+ tempOffsets.resize(stringsCount);
+ while (ui < stringsCount) {
offset = scriptS.readUint16();
// In some rooms in IHNM, string offsets can be greater than the maximum value than a 16-bit integer can hold
// We detect this by checking the previous offset, and if it was bigger than the current one, an overflow
@@ -423,27 +421,47 @@ void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPoin
if (prevOffset > offset)
offset += 65536;
prevOffset = offset;
- if (offset == stringsLength) {
- stringsCount = i;
- stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
+ if (offset == stringsData.size()) {
+ stringsCount = ui;
+ tempOffsets.resize(stringsCount);
break;
}
- if (offset > stringsLength) {
+ if (offset > stringsData.size()) {
// This case should never occur, but apparently it does in the Italian fan
// translation of IHNM
warning("SagaEngine::loadStrings wrong strings table");
- stringsCount = i;
- stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
+ stringsCount = ui;
+ tempOffsets.resize(stringsCount);
break;
}
- stringsTable.strings[i] = (const char *)stringsTable.stringsPointer + offset;
- debug(9, "string[%i]=%s", i, stringsTable.strings[i]);
- i++;
+ tempOffsets[ui] = offset;
+ ui++;
+ }
+
+ prevOffset = scriptS.pos();
+ int32 left = scriptS.size() - prevOffset;
+ if (left < 0) {
+ error("SagaEngine::loadStrings() Error loading strings buffer");
+ }
+
+ stringsTable.buffer.resize(left);
+ if (left > 0) {
+ scriptS.read(&stringsTable.buffer.front(), left);
+ }
+
+ stringsTable.strings.resize(tempOffsets.size());
+ for (ui = 0; ui < tempOffsets.size(); ui++) {
+ offset = tempOffsets[ui] - prevOffset;
+ if (offset >= stringsTable.buffer.size()) {
+ error("SagaEngine::loadStrings() Wrong offset");
+ }
+ stringsTable.strings[ui] = &stringsTable.buffer[offset];
+
+ debug(9, "string[%i]=%s", ui, stringsTable.strings[ui]);
}
- stringsTable.stringsCount = stringsCount;
}
-const char *SagaEngine::getObjectName(uint16 objectId) {
+const char *SagaEngine::getObjectName(uint16 objectId) const {
ActorData *actor;
ObjectData *obj;
const HitZone *hitZone;
@@ -598,7 +616,7 @@ void SagaEngine::setTalkspeed(int talkspeed) {
ConfMan.setInt("talkspeed", (talkspeed * 255 + 3 / 2) / 3);
}
-int SagaEngine::getTalkspeed() {
+int SagaEngine::getTalkspeed() const {
return (ConfMan.getInt("talkspeed") * 3 + 255 / 2) / 255;
}
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 102d1e5c82..149f1cb580 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -30,7 +30,7 @@
#include "common/array.h"
#include "common/random.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "sound/mididrv.h"
#include "saga/gfx.h"
@@ -49,7 +49,7 @@ struct ADGameFileDescription;
* SAGA2 status: in early stages of development, no recent activity. Contact sev
* if you want to work on it, since we have some original source codes.
*
- * Supported games:
+ * Games using this engine:
*
* SAGA:
* - Inherit the Earth
@@ -83,10 +83,7 @@ class Resource;
class ResourceContext;
-using Common::MemoryReadStream;
-using Common::MemoryReadStreamEndian;
-
-//#define SAGA_DEBUG 1 // define for test functions
+// #define SAGA_DEBUG 1 // define for test functions
#define SAGA_IMAGE_DATA_OFFSET 776
#define SAGA_IMAGE_HEADER_LEN 8
@@ -155,40 +152,40 @@ enum GameFeatures {
};
enum VerbTypeIds {
- kVerbITENone = 0,
- kVerbITEPickUp = 1,
- kVerbITELookAt = 2,
- kVerbITEWalkTo = 3,
- kVerbITETalkTo = 4,
- kVerbITEOpen = 5,
- kVerbITEClose = 6,
- kVerbITEGive = 7,
- kVerbITEUse = 8,
- kVerbITEOptions = 9,
- kVerbITEEnter = 10,
- kVerbITELeave = 11,
- kVerbITEBegin = 12,
- kVerbITEWalkOnly = 13,
- kVerbITELookOnly = 14,
-
-
- kVerbIHNMNone = 0,
- kVerbIHNMWalk = 1,
- kVerbIHNMLookAt = 2,
- kVerbIHNMTake = 3,
- kVerbIHNMUse = 4,
- kVerbIHNMTalkTo = 5,
- kVerbIHNMSwallow = 6,
- kVerbIHNMGive = 7,
- kVerbIHNMPush = 8,
- kVerbIHNMOptions = 9,
- kVerbIHNMEnter = 10,
- kVerbIHNMLeave = 11,
- kVerbIHNMBegin = 12,
- kVerbIHNMWalkOnly = 13,
- kVerbIHNMLookOnly = 14,
-
- kVerbTypeIdsMax = kVerbITELookOnly + 1
+ kVerbITENone = 0,
+ kVerbITEPickUp = 1,
+ kVerbITELookAt = 2,
+ kVerbITEWalkTo = 3,
+ kVerbITETalkTo = 4,
+ kVerbITEOpen = 5,
+ kVerbITEClose = 6,
+ kVerbITEGive = 7,
+ kVerbITEUse = 8,
+ kVerbITEOptions = 9,
+ kVerbITEEnter = 10,
+ kVerbITELeave = 11,
+ kVerbITEBegin = 12,
+ kVerbITEWalkOnly = 13,
+ kVerbITELookOnly = 14,
+
+
+ kVerbIHNMNone = 0,
+ kVerbIHNMWalk = 1,
+ kVerbIHNMLookAt = 2,
+ kVerbIHNMTake = 3,
+ kVerbIHNMUse = 4,
+ kVerbIHNMTalkTo = 5,
+ kVerbIHNMSwallow = 6,
+ kVerbIHNMGive = 7,
+ kVerbIHNMPush = 8,
+ kVerbIHNMOptions = 9,
+ kVerbIHNMEnter = 10,
+ kVerbIHNMLeave = 11,
+ kVerbIHNMBegin = 12,
+ kVerbIHNMWalkOnly = 13,
+ kVerbIHNMLookOnly = 14,
+
+ kVerbTypeIdsMax = kVerbITELookOnly + 1
};
enum PanelButtonType {
@@ -381,30 +378,21 @@ struct ImageHeader {
};
struct StringsTable {
- byte *stringsPointer;
- int stringsCount;
- const char **strings;
+ Common::Array<char> buffer;
+ Common::Array<char *> strings;
- const char *getString(int index) const {
- if ((stringsCount <= index) || (index < 0)) {
+ const char *getString(uint index) const {
+ if (strings.size() <= index) {
// This occurs at the end of Ted's chapter, right after the ending cutscene
- warning("StringsTable::getString wrong index 0x%X (%d)", index, stringsCount);
+ warning("StringsTable::getString wrong index 0x%X (%d)", index, strings.size());
return "";
}
return strings[index];
}
- void freeMem() {
- free(strings);
- free(stringsPointer);
- memset(this, 0, sizeof(*this));
- }
-
- StringsTable() {
- memset(this, 0, sizeof(*this));
- }
- ~StringsTable() {
- freeMem();
+ void clear() {
+ strings.clear();
+ buffer.clear();
}
};
@@ -467,6 +455,35 @@ inline uint16 objectIndexToId(int type, int index) {
return (type << OBJECT_TYPE_SHIFT) | (OBJECT_TYPE_MASK & index);
}
+class ByteArray : public Common::Array<byte> {
+public:
+ /**
+ * Return a pointer to the start of the buffer underlying this byte array,
+ * or NULL if the buffer is empty.
+ */
+ byte *getBuffer() {
+ return empty() ? NULL : &front();
+ }
+
+ const byte *getBuffer() const {
+ return empty() ? NULL : &front();
+ }
+
+ void assign(const ByteArray &src) {
+ resize(src.size());
+ if (!empty()) {
+ memcpy(&front(), &src.front(), size());
+ }
+ }
+};
+
+class ByteArrayReadStreamEndian : public Common::MemoryReadStreamEndian {
+public:
+ ByteArrayReadStreamEndian(const ByteArray & byteArray, bool bigEndian = false)
+ : Common::MemoryReadStreamEndian(byteArray.getBuffer(), byteArray.size(), bigEndian) {
+ }
+};
+
class SagaEngine : public Engine {
friend class Scene;
@@ -484,15 +501,14 @@ public:
void save(const char *fileName, const char *saveName);
void load(const char *fileName);
- uint32 getCurrentLoadVersion() {
+ uint32 getCurrentLoadVersion() const {
return _saveHeader.version;
}
void fillSaveList();
char *calcSaveFileName(uint slotNumber);
SaveFileData *getSaveFile(uint idx);
- uint getSaveSlotNumber(uint idx);
- uint getNewSaveSlotNumber();
+ uint getNewSaveSlotNumber() const;
bool locateSaveFile(char *saveName, uint &titleNumber);
bool isSaveListFull() const {
return _saveFilesCount == MAX_SAVES;
@@ -501,7 +517,7 @@ public:
return isSaveListFull() ? _saveFilesCount : _saveFilesCount + 1;
}
- bool isIHNMDemo() { return _isIHNMDemo; }
+ bool isIHNMDemo() const { return _isIHNMDemo; }
int16 _framesEsc;
@@ -546,23 +562,28 @@ public:
Common::RandomSource _rnd;
private:
- int decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outbuf, size_t outbuf_len);
- int flipImage(byte *img_buf, int columns, int scanlines);
- int unbankBGImage(byte *dest_buf, const byte *src_buf, int columns, int scanlines);
+ bool decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, ByteArray &outbuf);
+ void flipImage(byte *imageBuffer, int columns, int scanlines);
+ void unbankBGImage(byte *dest_buf, const byte *src_buf, int columns, int scanlines);
uint32 _previousTicks;
public:
- int decodeBGImage(const byte *image_data, size_t image_size,
- byte **output_buf, size_t *output_buf_len, int *w, int *h, bool flip = false);
- const byte *getImagePal(const byte *image_data, size_t image_size);
- void loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength);
+ bool decodeBGImage(const ByteArray &imageData, ByteArray &outputBuffer, int *w, int *h, bool flip = false);
+ const byte *getImagePal(const ByteArray &imageData) {
+ if (imageData.size() <= SAGA_IMAGE_HEADER_LEN) {
+ return NULL;
+ }
+
+ return &imageData.front() + SAGA_IMAGE_HEADER_LEN;
+ }
+ void loadStrings(StringsTable &stringsTable, const ByteArray &stringsData);
- const char *getObjectName(uint16 objectId);
+ const char *getObjectName(uint16 objectId) const;
public:
int processInput();
Point mousePos() const;
- int getMouseClickCount() {
+ int getMouseClickCount() const {
return _mouseClickCount;
}
@@ -586,7 +607,7 @@ public:
return _leftMouseButtonPressed || _rightMouseButtonPressed;
}
- inline int ticksToMSec(int tick) {
+ inline int ticksToMSec(int tick) const {
if (getGameId() == GID_ITE)
return tick * 1000 / kScriptTimeTicksPerSecond;
else
@@ -617,9 +638,9 @@ public:
bool isBigEndian() const;
bool isMacResources() const;
bool isSaga2() const { return getGameId() == GID_DINO || getGameId() == GID_FTA2; }
- const GameResourceDescription *getResourceDescription();
+ const GameResourceDescription *getResourceDescription() const;
- const GameFontDescription *getFontDescription(int index);
+ const GameFontDescription *getFontDescription(int index) const;
int getFontsCount() const;
int getGameId() const;
@@ -648,7 +669,7 @@ private:
public:
ColorId KnownColor2ColorId(KnownColor knownColor);
void setTalkspeed(int talkspeed);
- int getTalkspeed();
+ int getTalkspeed() const;
};
} // End of namespace Saga
diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp
index 2740462dab..c5388d6878 100644
--- a/engines/saga/saveload.cpp
+++ b/engines/saga/saveload.cpp
@@ -82,7 +82,7 @@ bool SagaEngine::locateSaveFile(char *saveName, uint &titleNumber) {
return false;
}
-uint SagaEngine::getNewSaveSlotNumber() {
+uint SagaEngine::getNewSaveSlotNumber() const {
uint i, j;
bool found;
for (i = 0; i < MAX_SAVES; i++) {
@@ -240,9 +240,9 @@ void SagaEngine::save(const char *fileName, const char *saveName) {
_actor->saveState(out);
- out->writeSint16LE(_script->_commonBufferSize);
+ out->writeSint16LE(_script->_commonBuffer.size());
- out->write(_script->_commonBuffer, _script->_commonBufferSize);
+ out->write(_script->_commonBuffer.getBuffer(), _script->_commonBuffer.size());
// ISO map x, y coordinates for ITE
if (getGameId() == GID_ITE) {
@@ -282,7 +282,7 @@ void SagaEngine::load(const char *fileName) {
_saveHeader.version = SWAP_BYTES_32(_saveHeader.version);
}
- debug(2, "Save version: %x", _saveHeader.version);
+ debug(2, "Save version: 0x%X", _saveHeader.version);
if (_saveHeader.version < 4)
warning("This savegame is not endian-safe. There may be problems");
@@ -351,7 +351,8 @@ void SagaEngine::load(const char *fileName) {
_actor->loadState(in);
commonBufferSize = in->readSint16LE();
- in->read(_script->_commonBuffer, commonBufferSize);
+ _script->_commonBuffer.resize(commonBufferSize);
+ in->read(_script->_commonBuffer.getBuffer(), commonBufferSize);
if (getGameId() == GID_ITE) {
mapx = in->readSint16LE();
diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp
index 2887d79693..8e9e4463ba 100644
--- a/engines/saga/scene.cpp
+++ b/engines/saga/scene.cpp
@@ -137,10 +137,9 @@ const char *SAGAResourceTypesString[] = {
};
Scene::Scene(SagaEngine *vm) : _vm(vm) {
- byte *sceneLUTPointer;
- size_t sceneLUTLength;
+ ByteArray sceneLUTData;
uint32 resourceId;
- int i;
+ uint i;
// Do nothing for SAGA2 games for now
if (_vm->isSaga2()) {
@@ -158,74 +157,62 @@ Scene::Scene(SagaEngine *vm) : _vm(vm) {
// Load scene lookup table
resourceId = _vm->_resource->convertResourceId(_vm->getResourceDescription()->sceneLUTResourceId);
debug(3, "Loading scene LUT from resource %i", resourceId);
- _vm->_resource->loadResource(_sceneContext, resourceId, sceneLUTPointer, sceneLUTLength);
- if (sceneLUTLength == 0) {
- error("Scene::Scene() sceneLUTLength == 0");
- }
- _sceneCount = sceneLUTLength / 2;
- _sceneLUT = (int *)malloc(_sceneCount * sizeof(*_sceneLUT));
- if (_sceneLUT == NULL) {
- memoryError("Scene::Scene()");
+ _vm->_resource->loadResource(_sceneContext, resourceId, sceneLUTData);
+ if (sceneLUTData.empty()) {
+ error("Scene::Scene() sceneLUT is empty");
}
+ _sceneLUT.resize(sceneLUTData.size() / 2);
- MemoryReadStreamEndian readS(sceneLUTPointer, sceneLUTLength, _sceneContext->isBigEndian());
+ ByteArrayReadStreamEndian readS(sceneLUTData, _sceneContext->isBigEndian());
- for (i = 0; i < _sceneCount; i++) {
+ for (i = 0; i < _sceneLUT.size(); i++) {
_sceneLUT[i] = readS.readUint16();
debug(8, "sceneNumber %i has resourceId %i", i, _sceneLUT[i]);
}
- free(sceneLUTPointer);
-
#ifdef SAGA_DEBUG
#define DUMP_SCENES_LEVEL 10
if (DUMP_SCENES_LEVEL <= gDebugLevel) {
- uint j;
int backUpDebugLevel = gDebugLevel;
SAGAResourceTypes *types;
int typesCount;
SAGAResourceTypes resType;
+ SceneResourceDataArray resourceList;
getResourceTypes(types, typesCount);
- for (i = 0; i < _sceneCount; i++) {
+ for (i = 0; i < _sceneLUT.size(); i++) {
gDebugLevel = -1;
loadSceneDescriptor(_sceneLUT[i]);
- loadSceneResourceList(_sceneDescription.resourceListResourceId);
+ loadSceneResourceList(_sceneDescription.resourceListResourceId, resourceList);
gDebugLevel = backUpDebugLevel;
debug(DUMP_SCENES_LEVEL, "Dump Scene: number %i, descriptor resourceId %i, resourceList resourceId %i", i, _sceneLUT[i], _sceneDescription.resourceListResourceId);
- debug(DUMP_SCENES_LEVEL, "\tresourceListCount %i", (int)_resourceListCount);
- for (j = 0; j < _resourceListCount; j++) {
- if (_resourceList[j].resourceType >= typesCount) {
- error("wrong resource type %i", _resourceList[j].resourceType);
+ debug(DUMP_SCENES_LEVEL, "\tresourceListCount %i", (int)resourceList.size());
+ for (SceneResourceDataArray::iterator j = resourceList.begin(); j != resourceList.end(); ++j) {
+ if (j->resourceType >= typesCount) {
+ error("wrong resource type %i", j->resourceType);
}
- resType = types[_resourceList[j].resourceType];
+ resType = types[j->resourceType];
- debug(DUMP_SCENES_LEVEL, "\t%s resourceId %i", SAGAResourceTypesString[resType], _resourceList[j].resourceId);
+ debug(DUMP_SCENES_LEVEL, "\t%s resourceId %i", SAGAResourceTypesString[resType], j->resourceId);
}
- free(_resourceList);
}
}
#endif
- debug(3, "LUT has %d entries.", _sceneCount);
+ debug(3, "LUT has %d entries.", _sceneLUT.size());
_sceneLoaded = false;
_sceneNumber = 0;
_chapterNumber = 0;
_sceneResourceId = 0;
_inGame = false;
- _loadDescription = false;
- memset(&_sceneDescription, 0, sizeof(_sceneDescription));
- _resourceListCount = 0;
- _resourceList = NULL;
+ _sceneDescription.reset();
_sceneProc = NULL;
_objectMap = new ObjectMap(_vm);
_actionMap = new ObjectMap(_vm);
- memset(&_bg, 0, sizeof(_bg));
- memset(&_bgMask, 0, sizeof(_bgMask));
}
Scene::~Scene() {
@@ -236,7 +223,6 @@ Scene::~Scene() {
delete _actionMap;
delete _objectMap;
- free(_sceneLUT);
}
void Scene::getResourceTypes(SAGAResourceTypes *&types, int &typesCount) {
@@ -281,7 +267,7 @@ void Scene::startScene() {
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
switch (_vm->getGameId()) {
case GID_ITE:
@@ -528,8 +514,7 @@ void Scene::getSlopes(int &beginSlope, int &endSlope) {
}
void Scene::getBGInfo(BGInfo &bgInfo) {
- bgInfo.buffer = _bg.buf;
- bgInfo.bufferLength = _bg.buf_len;
+ bgInfo.buffer = _bg.buffer.getBuffer();
bgInfo.bounds.left = 0;
bgInfo.bounds.top = 0;
@@ -581,15 +566,14 @@ bool Scene::offscreenPath(Point &testPoint) {
}
-void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength) {
+void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer) {
if (!_bgMask.loaded) {
error("Scene::getBGMaskInfo _bgMask not loaded");
}
width = _bgMask.w;
height = _bgMask.h;
- buffer = _bgMask.buf;
- bufferLength = _bgMask.buf_len;
+ buffer = _bgMask.buffer.getBuffer();
}
void Scene::initDoorsState() {
@@ -597,9 +581,8 @@ void Scene::initDoorsState() {
}
void Scene::loadScene(LoadSceneParams &loadSceneParams) {
- size_t i;
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static PalEntry current_pal[PAL_ENTRIES];
if (loadSceneParams.transitionType == kTransitionFade)
@@ -610,7 +593,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.code = kCursorEvent;
event.op = kEventSetBusyCursor;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_chapterPointsChanged = false;
@@ -623,8 +606,8 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
if (loadSceneParams.chapter == 6 || loadSceneParams.chapter == 8)
_vm->_interface->setLeftPortrait(0);
- _vm->_anim->freeCutawayList();
- _vm->_script->freeModules();
+ _vm->_anim->clearCutawayList();
+ _vm->_script->clearModules();
// deleteAllScenes();
@@ -659,8 +642,6 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
error("Scene::loadScene(): Error, a scene is already loaded");
}
- _loadDescription = true;
-
#ifdef ENABLE_IHNM
if (_vm->getGameId() == GID_IHNM) {
if (loadSceneParams.loadFlag == kLoadBySceneNumber) // When will we get rid of it?
@@ -678,16 +659,6 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
_sceneNumber = loadSceneParams.sceneDescriptor;
_sceneResourceId = getSceneResourceId(_sceneNumber);
break;
- case kLoadByDescription:
- _sceneNumber = -1;
- _sceneResourceId = -1;
- assert(loadSceneParams.sceneDescription != NULL);
- assert(loadSceneParams.sceneDescription->resourceList != NULL);
- _loadDescription = false;
- _sceneDescription = *loadSceneParams.sceneDescription;
- _resourceList = loadSceneParams.sceneDescription->resourceList;
- _resourceListCount = loadSceneParams.sceneDescription->resourceListCount;
- break;
}
debug(3, "Loading scene number %d:", _sceneNumber);
@@ -702,34 +673,15 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
}
// Load scene descriptor and resource list resources
- if (_loadDescription) {
- debug(3, "Loading scene resource %i", _sceneResourceId);
-
- loadSceneDescriptor(_sceneResourceId);
-
- loadSceneResourceList(_sceneDescription.resourceListResourceId);
- } else {
- debug(3, "Loading memory scene resource");
- }
+ debug(3, "Loading scene resource %i", _sceneResourceId);
- // Load resources from scene resource list
- for (i = 0; i < _resourceListCount; i++) {
- if (!_resourceList[i].invalid) {
- _vm->_resource->loadResource(_sceneContext, _resourceList[i].resourceId,
- _resourceList[i].buffer, _resourceList[i].size);
+ loadSceneDescriptor(_sceneResourceId);
-
- if (_resourceList[i].size >= 6) {
- if (!memcmp(_resourceList[i].buffer, "DUMMY!", 6)) {
- _resourceList[i].invalid = true;
- warning("DUMMY resource %i", _resourceList[i].resourceId);
- }
- }
- }
- }
+ SceneResourceDataArray resourceList;
+ loadSceneResourceList(_sceneDescription.resourceListResourceId, resourceList);
// Process resources from scene resource list
- processSceneResources();
+ processSceneResources(resourceList);
if (_sceneDescription.flags & kSceneFlagISO) {
_outsetSceneNumber = _sceneNumber;
@@ -748,7 +700,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
_sceneLoaded = true;
- q_event = NULL;
+ eventColumns = NULL;
if (loadSceneParams.transitionType == kTransitionFade) {
@@ -762,7 +714,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = current_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -771,7 +723,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Display scene background, but stay with black palette
event.type = kEvTImmediate;
@@ -780,7 +732,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kEvPNoSetPalette;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
@@ -796,7 +748,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param4 = _sceneNumber; // Object
event.param5 = loadSceneParams.actorsEntrance; // With Object
event.param6 = 0; // Actor
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
}
if (loadSceneParams.transitionType == kTransitionFade) {
@@ -808,7 +760,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kFadeIn;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
// Fade in from black to the scene background palette
event.type = kEvTImmediate;
@@ -817,7 +769,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = _bg.pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -826,7 +778,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
if (loadSceneParams.sceneProc == NULL) {
@@ -845,13 +797,13 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param2 = MUSIC_DEFAULT;
event.op = kEventPlay;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
} else {
event.type = kEvTOneshot;
event.code = kMusicEvent;
event.op = kEventStop;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
}
@@ -861,14 +813,14 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.op = kEventDisplay;
event.param = kEvPSetPalette;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
// Begin palette cycle animation if present
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ _vm->_events->queue(event);
// Start the scene main script
if (_sceneDescription.sceneScriptEntrypointNumber > 0) {
@@ -882,7 +834,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param4 = _sceneNumber; // Object
event.param5 = loadSceneParams.actorsEntrance; // With Object
event.param6 = 0; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
debug(3, "Scene started");
@@ -905,7 +857,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.code = kInterfaceEvent;
event.op = kEventActivate;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
// Change the cursor back to a crosshair in IHNM
@@ -913,23 +865,22 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.code = kCursorEvent;
event.op = kEventSetNormalCursor;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
void Scene::loadSceneDescriptor(uint32 resourceId) {
- byte *sceneDescriptorData;
- size_t sceneDescriptorDataLength;
+ ByteArray sceneDescriptorData;
- memset(&_sceneDescription, 0, sizeof(_sceneDescription));
+ _sceneDescription.reset();
if (resourceId == 0) {
return;
}
- _vm->_resource->loadResource(_sceneContext, resourceId, sceneDescriptorData, sceneDescriptorDataLength);
+ _vm->_resource->loadResource(_sceneContext, resourceId, sceneDescriptorData);
- if (sceneDescriptorDataLength == 16) {
- MemoryReadStreamEndian readS(sceneDescriptorData, sceneDescriptorDataLength, _sceneContext->isBigEndian());
+ if (sceneDescriptorData.size() == 16) {
+ ByteArrayReadStreamEndian readS(sceneDescriptorData, _sceneContext->isBigEndian());
_sceneDescription.flags = readS.readSint16();
_sceneDescription.resourceListResourceId = readS.readSint16();
@@ -940,53 +891,44 @@ void Scene::loadSceneDescriptor(uint32 resourceId) {
_sceneDescription.startScriptEntrypointNumber = readS.readUint16();
_sceneDescription.musicResourceId = readS.readSint16();
}
-
- free(sceneDescriptorData);
}
-void Scene::loadSceneResourceList(uint32 resourceId) {
- byte *resourceListData;
- size_t resourceListDataLength;
- size_t i;
+void Scene::loadSceneResourceList(uint32 resourceId, SceneResourceDataArray &resourceList) {
+ ByteArray resourceListData;
- _resourceListCount = 0;
- _resourceList = NULL;
+ resourceList.clear();
if (resourceId == 0) {
return;
}
// Load the scene resource table
- _vm->_resource->loadResource(_sceneContext, resourceId, resourceListData, resourceListDataLength);
+ _vm->_resource->loadResource(_sceneContext, resourceId, resourceListData);
- if ((resourceListDataLength % SAGA_RESLIST_ENTRY_LEN) == 0) {
- MemoryReadStreamEndian readS(resourceListData, resourceListDataLength, _sceneContext->isBigEndian());
+ if ((resourceListData.size() % SAGA_RESLIST_ENTRY_LEN) == 0) {
+ ByteArrayReadStreamEndian readS(resourceListData, _sceneContext->isBigEndian());
// Allocate memory for scene resource list
- _resourceListCount = resourceListDataLength / SAGA_RESLIST_ENTRY_LEN;
- debug(3, "Scene resource list contains %i entries", (int)_resourceListCount);
- _resourceList = (SceneResourceData *)calloc(_resourceListCount, sizeof(*_resourceList));
+ resourceList.resize(resourceListData.size() / SAGA_RESLIST_ENTRY_LEN);
+ debug(3, "Scene resource list contains %i entries", (int)resourceList.size());
// Load scene resource list from raw scene
// resource table
debug(3, "Loading scene resource list");
- for (i = 0; i < _resourceListCount; i++) {
- _resourceList[i].resourceId = readS.readUint16();
- _resourceList[i].resourceType = readS.readUint16();
+ for (SceneResourceDataArray::iterator resource = resourceList.begin(); resource != resourceList.end(); ++resource) {
+ resource->resourceId = readS.readUint16();
+ resource->resourceType = readS.readUint16();
// demo version may contain invalid resourceId
- _resourceList[i].invalid = !_sceneContext->validResourceId(_resourceList[i].resourceId);
+ resource->invalid = !_sceneContext->validResourceId(resource->resourceId);
}
}
- free(resourceListData);
}
-void Scene::processSceneResources() {
- byte *resourceData;
- size_t resourceDataLength;
+void Scene::processSceneResources(SceneResourceDataArray &resourceList) {
+ ByteArray resourceData;
const byte *palPointer;
- size_t i;
SAGAResourceTypes *types = 0;
int typesCount = 0;
SAGAResourceTypes resType;
@@ -994,22 +936,33 @@ void Scene::processSceneResources() {
getResourceTypes(types, typesCount);
// Process the scene resource list
- for (i = 0; i < _resourceListCount; i++) {
- if (_resourceList[i].invalid) {
+ for (SceneResourceDataArray::iterator resource = resourceList.begin(); resource != resourceList.end(); ++resource) {
+ if (resource->invalid) {
+ continue;
+ }
+ _vm->_resource->loadResource(_sceneContext, resource->resourceId, resourceData);
+
+
+ if (resourceData.size() >= 6) {
+ if (!memcmp(resourceData.getBuffer(), "DUMMY!", 6)) {
+ resource->invalid = true;
+ warning("DUMMY resource %i", resource->resourceId);
+ }
+ }
+
+ if (resource->invalid) {
continue;
}
- resourceData = _resourceList[i].buffer;
- resourceDataLength = _resourceList[i].size;
- if (_resourceList[i].resourceType >= typesCount) {
- error("Scene::processSceneResources() wrong resource type %i", _resourceList[i].resourceType);
+ if (resource->resourceType >= typesCount) {
+ error("Scene::processSceneResources() wrong resource type %i", resource->resourceType);
}
- resType = types[_resourceList[i].resourceType];
+ resType = types[resource->resourceType];
switch (resType) {
case SAGA_UNKNOWN:
- warning("UNKNOWN resourceType %i", _resourceList[i].resourceType);
+ warning("UNKNOWN resourceType %i", resource->resourceType);
break;
case SAGA_ACTOR:
//for (a = actorsInScene; a; a = a->nextInScene)
@@ -1027,20 +980,16 @@ void Scene::processSceneResources() {
}
debug(3, "Loading background resource.");
- _bg.res_buf = resourceData;
- _bg.res_len = resourceDataLength;
- _bg.loaded = 1;
-
- if (_vm->decodeBGImage(_bg.res_buf,
- _bg.res_len,
- &_bg.buf,
- &_bg.buf_len,
+
+ if (!_vm->decodeBGImage(resourceData,
+ _bg.buffer,
&_bg.w,
- &_bg.h) != SUCCESS) {
- error("Scene::processSceneResources() Error loading background resource %i", _resourceList[i].resourceId);
+ &_bg.h)) {
+ error("Scene::processSceneResources() Error loading background resource %i", resource->resourceId);
}
+ _bg.loaded = true;
- palPointer = _vm->getImagePal(_bg.res_buf, _bg.res_len);
+ palPointer = _vm->getImagePal(resourceData);
memcpy(_bg.pal, palPointer, sizeof(_bg.pal));
break;
case SAGA_BG_MASK: // Scene background mask resource
@@ -1048,30 +997,27 @@ void Scene::processSceneResources() {
error("Scene::ProcessSceneResources(): Duplicate background mask resource encountered");
}
debug(3, "Loading BACKGROUND MASK resource.");
- _bgMask.res_buf = resourceData;
- _bgMask.res_len = resourceDataLength;
- _bgMask.loaded = 1;
- _vm->decodeBGImage(_bgMask.res_buf, _bgMask.res_len, &_bgMask.buf,
- &_bgMask.buf_len, &_bgMask.w, &_bgMask.h, true);
+ _vm->decodeBGImage(resourceData, _bgMask.buffer, &_bgMask.w, &_bgMask.h, true);
+ _bgMask.loaded = true;
// At least in ITE the mask needs to be clipped.
_bgMask.w = MIN(_bgMask.w, _vm->getDisplayInfo().width);
_bgMask.h = MIN(_bgMask.h, getHeight());
- debug(4, "BACKGROUND MASK width=%d height=%d length=%d", _bgMask.w, _bgMask.h, (int)_bgMask.buf_len);
+ debug(4, "BACKGROUND MASK width=%d height=%d length=%d", _bgMask.w, _bgMask.h, _bgMask.buffer.size());
break;
case SAGA_STRINGS:
debug(3, "Loading scene strings resource...");
- _vm->loadStrings(_sceneStrings, resourceData, resourceDataLength);
+ _vm->loadStrings(_sceneStrings, resourceData);
break;
case SAGA_OBJECT_MAP:
debug(3, "Loading object map resource...");
- _objectMap->load(resourceData, resourceDataLength);
+ _objectMap->load(resourceData);
break;
case SAGA_ACTION_MAP:
debug(3, "Loading action map resource...");
- _actionMap->load(resourceData, resourceDataLength);
+ _actionMap->load(resourceData);
break;
case SAGA_ISO_IMAGES:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1080,7 +1026,7 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric images resource.");
- _vm->_isoMap->loadImages(resourceData, resourceDataLength);
+ _vm->_isoMap->loadImages(resourceData);
break;
case SAGA_ISO_MAP:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1089,7 +1035,7 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric map resource.");
- _vm->_isoMap->loadMap(resourceData, resourceDataLength);
+ _vm->_isoMap->loadMap(resourceData);
break;
case SAGA_ISO_PLATFORMS:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1098,7 +1044,7 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric platforms resource.");
- _vm->_isoMap->loadPlatforms(resourceData, resourceDataLength);
+ _vm->_isoMap->loadPlatforms(resourceData);
break;
case SAGA_ISO_METATILES:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1107,20 +1053,20 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric metatiles resource.");
- _vm->_isoMap->loadMetaTiles(resourceData, resourceDataLength);
+ _vm->_isoMap->loadMetaTiles(resourceData);
break;
case SAGA_ANIM:
{
- uint16 animId = _resourceList[i].resourceType - 14;
+ uint16 animId = resource->resourceType - 14;
debug(3, "Loading animation resource animId=%i", animId);
- _vm->_anim->load(animId, resourceData, resourceDataLength);
+ _vm->_anim->load(animId, resourceData);
}
break;
case SAGA_ENTRY:
debug(3, "Loading entry list resource...");
- loadSceneEntryList(resourceData, resourceDataLength);
+ loadSceneEntryList(resourceData);
break;
case SAGA_ISO_MULTI:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1129,23 +1075,23 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric multi resource.");
- _vm->_isoMap->loadMulti(resourceData, resourceDataLength);
+ _vm->_isoMap->loadMulti(resourceData);
break;
case SAGA_PAL_ANIM:
debug(3, "Loading palette animation resource.");
- _vm->_palanim->loadPalAnim(resourceData, resourceDataLength);
+ _vm->_palanim->loadPalAnim(resourceData);
break;
case SAGA_FACES:
if (_vm->getGameId() == GID_ITE)
- _vm->_interface->loadScenePortraits(_resourceList[i].resourceId);
+ _vm->_interface->loadScenePortraits(resource->resourceId);
break;
case SAGA_PALETTE:
{
PalEntry pal[PAL_ENTRIES];
- byte *palPtr = resourceData;
+ byte *palPtr = resourceData.getBuffer();
- if (resourceDataLength < 3 * PAL_ENTRIES)
- error("Too small scene palette %i", (int)resourceDataLength);
+ if (resourceData.size() < 3 * PAL_ENTRIES)
+ error("Too small scene palette %i", (int)resourceData.size());
for (uint16 c = 0; c < PAL_ENTRIES; c++) {
pal[c].red = *palPtr++;
@@ -1156,7 +1102,7 @@ void Scene::processSceneResources() {
}
break;
default:
- error("Scene::ProcessSceneResources() Encountered unknown resource type %i", _resourceList[i].resourceType);
+ error("Scene::ProcessSceneResources() Encountered unknown resource type %i", resource->resourceType);
break;
}
}
@@ -1184,7 +1130,6 @@ void Scene::draw() {
void Scene::endScene() {
Rect rect;
- size_t i;
if (!_sceneLoaded)
return;
@@ -1222,37 +1167,28 @@ void Scene::endScene() {
// Free scene background
if (_bg.loaded) {
- free(_bg.buf);
- _bg.loaded = 0;
+ _bg.buffer.clear();
+ _bg.loaded = false;
}
// Free scene background mask
if (_bgMask.loaded) {
- free(_bgMask.buf);
- _bgMask.loaded = 0;
- }
-
- // Free scene resource list
- for (i = 0; i < _resourceListCount; i++) {
- free(_resourceList[i].buffer);
- }
-
- if (_loadDescription) {
- free(_resourceList);
+ _bgMask.buffer.clear();
+ _bgMask.loaded = false;
}
// Free animation info list
_vm->_anim->reset();
- _vm->_palanim->freePalAnim();
+ _vm->_palanim->clear();
- _objectMap->freeMem();
- _actionMap->freeMem();
- _entryList.freeMem();
- _sceneStrings.freeMem();
+ _objectMap->clear();
+ _actionMap->clear();
+ _entryList.clear();
+ _sceneStrings.clear();
if (_vm->getGameId() == GID_ITE)
- _vm->_isoMap->freeMem();
+ _vm->_isoMap->clear();
_vm->_events->clearList();
_textList.clear();
@@ -1275,7 +1211,7 @@ void Scene::restoreScene() {
event.param = kEvPNoSetPalette;
event.time = 0;
event.duration = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_vm->_gfx->showCursor(true);
}
@@ -1285,7 +1221,7 @@ void Scene::cmdSceneChange(int argc, const char **argv) {
scene_num = atoi(argv[1]);
- if ((scene_num < 1) || (scene_num >= _sceneCount)) {
+ if ((scene_num < 1) || (uint(scene_num) >= _sceneLUT.size())) {
_vm->_console->DebugPrintf("Invalid scene number.\n");
return;
}
@@ -1303,34 +1239,29 @@ void Scene::cmdObjectMapInfo() {
_objectMap->cmdInfo();
}
-void Scene::loadSceneEntryList(const byte* resourcePointer, size_t resourceLength) {
- int i;
-
- _entryList.entryListCount = resourceLength / 8;
-
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _sceneContext->isBigEndian());
+void Scene::loadSceneEntryList(const ByteArray &resourceData) {
+ uint i;
+ if (!_entryList.empty()) {
+ error("Scene::loadSceneEntryList entryList not empty");
+ }
- if (_entryList.entryList)
- error("Scene::loadSceneEntryList entryList != NULL");
+ _entryList.resize(resourceData.size() / 8);
- _entryList.entryList = (SceneEntry *) malloc(_entryList.entryListCount * sizeof(*_entryList.entryList));
- if (_entryList.entryList == NULL) {
- memoryError("Scene::loadSceneEntryList");
- }
+ ByteArrayReadStreamEndian readS(resourceData, _sceneContext->isBigEndian());
- for (i = 0; i < _entryList.entryListCount; i++) {
- _entryList.entryList[i].location.x = readS.readSint16();
- _entryList.entryList[i].location.y = readS.readSint16();
- _entryList.entryList[i].location.z = readS.readSint16();
- _entryList.entryList[i].facing = readS.readUint16();
+ for (i = 0; i < _entryList.size(); i++) {
+ _entryList[i].location.x = readS.readSint16();
+ _entryList[i].location.y = readS.readSint16();
+ _entryList[i].location.z = readS.readSint16();
+ _entryList[i].facing = readS.readUint16();
}
}
void Scene::clearPlacard() {
static PalEntry cur_pal[PAL_ENTRIES];
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
_vm->_interface->setFadeMode(kFadeOut);
@@ -1342,7 +1273,7 @@ void Scene::clearPlacard() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -1351,14 +1282,14 @@ void Scene::clearPlacard() {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
if (_vm->getGameId() == GID_ITE) {
event.type = kEvTOneshot;
event.code = kTextEvent;
event.op = kEventRemove;
event.data = _vm->_script->getPlacardTextEntry();
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
} else {
_vm->_scene->_textList.clear();
}
@@ -1368,7 +1299,7 @@ void Scene::clearPlacard() {
event.op = kEventRestoreMode;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
#ifdef ENABLE_IHNM
if (_vm->getGameId() == GID_IHNM) {
@@ -1379,7 +1310,7 @@ void Scene::clearPlacard() {
event.param = kPanelMain;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
#endif
@@ -1390,7 +1321,7 @@ void Scene::clearPlacard() {
event.param = kEvPNoSetPalette;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1399,7 +1330,7 @@ void Scene::clearPlacard() {
event.param = kFadeIn;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Fade in from black to the scene background palette
event.type = kEvTImmediate;
@@ -1408,7 +1339,7 @@ void Scene::clearPlacard() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = _bg.pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1417,18 +1348,18 @@ void Scene::clearPlacard() {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventShow;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypePlacard;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
#ifdef ENABLE_IHNM
@@ -1439,7 +1370,7 @@ void Scene::showPsychicProfile(const char *text) {
PalEntry *pal;
TextListEntry textEntry;
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
if (_vm->_interface->getMode() == kPanelPlacard)
return;
@@ -1453,7 +1384,7 @@ void Scene::showPsychicProfile(const char *text) {
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
_vm->_interface->setFadeMode(kFadeOut);
@@ -1465,7 +1396,7 @@ void Scene::showPsychicProfile(const char *text) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1474,17 +1405,17 @@ void Scene::showPsychicProfile(const char *text) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kInterfaceEvent;
event.op = kEventClearStatus;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Set the background and palette for the psychic profile
event.type = kEvTOneshot;
event.code = kPsychicProfileBgEvent;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
_vm->_scene->_textList.clear();
@@ -1507,7 +1438,7 @@ void Scene::showPsychicProfile(const char *text) {
event.code = kTextEvent;
event.op = kEventDisplay;
event.data = _psychicProfileTextEntry;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
_vm->_scene->getBGPal(pal);
@@ -1518,13 +1449,13 @@ void Scene::showPsychicProfile(const char *text) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypePlacard;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
void Scene::clearPsychicProfile() {
diff --git a/engines/saga/scene.h b/engines/saga/scene.h
index 0131e01abb..8d48e71445 100644
--- a/engines/saga/scene.h
+++ b/engines/saga/scene.h
@@ -32,6 +32,7 @@
#include "saga/actor.h"
#include "saga/interface.h"
#include "saga/puzzle.h"
+#include "saga/events.h"
namespace Saga {
@@ -55,8 +56,6 @@ namespace Saga {
class ObjectMap;
-struct Event;
-
enum SceneFlags {
kSceneFlagISO = 1,
kSceneFlagShowCursor = 2
@@ -74,7 +73,6 @@ enum FTA2Endings {
struct BGInfo {
Rect bounds;
byte *buffer;
- size_t bufferLength;
};
typedef int (SceneProc) (int, void *);
@@ -112,11 +110,14 @@ enum SAGAResourceTypes {
struct SceneResourceData {
uint32 resourceId;
int resourceType;
- byte *buffer;
- size_t size;
bool invalid;
+
+ SceneResourceData() : resourceId(0), resourceType(0), invalid(false) {
+ }
};
+typedef Common::Array<SceneResourceData> SceneResourceDataArray;
+
#define SAGA_SCENE_DESC_LEN 16
struct SceneDescription {
@@ -128,47 +129,31 @@ struct SceneDescription {
uint16 sceneScriptEntrypointNumber;
uint16 startScriptEntrypointNumber;
int16 musicResourceId;
- SceneResourceData *resourceList;
- size_t resourceListCount;
+
+ void reset() {
+ flags = resourceListResourceId = endSlope = beginSlope = scriptModuleNumber = sceneScriptEntrypointNumber = startScriptEntrypointNumber = musicResourceId = 0;
+ }
};
struct SceneEntry {
Location location;
- int facing;
+ uint16 facing;
};
-struct SceneEntryList {
- SceneEntry *entryList;
- int entryListCount;
-
- const SceneEntry * getEntry(int index) {
- if ((index < 0) || (index >= entryListCount)) {
- error("SceneEntryList::getEntry wrong index (%d)", index);
- }
- return &entryList[index];
- }
- void freeMem() {
- free(entryList);
- memset(this, 0, sizeof(*this));
- }
- SceneEntryList() {
- memset(this, 0, sizeof(*this));
- }
- ~SceneEntryList() {
- freeMem();
- }
+class SceneEntryList : public Common::Array<SceneEntry> {
};
struct SceneImage {
- int loaded;
+ bool loaded;
int w;
int h;
int p;
- byte *buf;
- size_t buf_len;
- byte *res_buf;
- size_t res_len;
+ ByteArray buffer;
PalEntry pal[256];
+
+ SceneImage() : loaded(false), w(0), h(0), p(0) {
+ memset(pal, 0, sizeof(pal));
+ }
};
@@ -179,14 +164,12 @@ enum SceneTransitionType {
enum SceneLoadFlags {
kLoadByResourceId,
- kLoadBySceneNumber,
- kLoadByDescription
+ kLoadBySceneNumber
};
struct LoadSceneParams {
int32 sceneDescriptor;
SceneLoadFlags loadFlag;
- SceneDescription* sceneDescription;
SceneProc *sceneProc;
bool sceneSkipTarget;
SceneTransitionType transitionType;
@@ -248,8 +231,8 @@ class Scene {
void skipScene();
void endScene();
void restoreScene();
- void queueScene(LoadSceneParams *sceneQueue) {
- _sceneQueue.push_back(*sceneQueue);
+ void queueScene(const LoadSceneParams &sceneQueue) {
+ _sceneQueue.push_back(sceneQueue);
}
void draw();
@@ -258,7 +241,7 @@ class Scene {
bool isInIntro() { return !_inGame; }
const Rect& getSceneClip() const { return _sceneClip; }
- void getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength);
+ void getBGMaskInfo(int &width, int &height, byte *&buffer);
int isBGMaskPresent() { return _bgMask.loaded; }
int getBGMaskType(const Point &testPoint) {
@@ -274,7 +257,7 @@ class Scene {
}
#endif
- return (_bgMask.buf[offset] >> 4) & 0x0f;
+ return (_bgMask.buffer[offset] >> 4) & 0x0f;
}
bool validBGMaskPoint(const Point &testPoint) {
@@ -325,7 +308,7 @@ class Scene {
bool isSceneLoaded() const { return _sceneLoaded; }
- int getSceneResourceId(int sceneNumber) {
+ uint16 getSceneResourceId(int sceneNumber) {
#ifdef SCENE_DEBUG
if ((sceneNumber < 0) || (sceneNumber >= _sceneCount)) {
error("getSceneResourceId: wrong sceneNumber %i", sceneNumber);
@@ -376,17 +359,16 @@ class Scene {
private:
void loadScene(LoadSceneParams &loadSceneParams);
void loadSceneDescriptor(uint32 resourceId);
- void loadSceneResourceList(uint32 resourceId);
- void loadSceneEntryList(const byte* resourcePointer, size_t resourceLength);
- void processSceneResources();
+ void loadSceneResourceList(uint32 resourceId, SceneResourceDataArray &resourceList);
+ void loadSceneEntryList(const ByteArray &resourceData);
+ void processSceneResources(SceneResourceDataArray &resourceList);
void getResourceTypes(SAGAResourceTypes *&types, int &typesCount);
SagaEngine *_vm;
ResourceContext *_sceneContext;
- int *_sceneLUT;
- int _sceneCount;
+ Common::Array<uint16> _sceneLUT;
SceneQueueList _sceneQueue;
bool _sceneLoaded;
int _currentProtag;
@@ -398,10 +380,7 @@ class Scene {
int _currentMusicRepeat;
bool _chapterPointsChanged;
bool _inGame;
- bool _loadDescription;
SceneDescription _sceneDescription;
- size_t _resourceListCount;
- SceneResourceData *_resourceList;
SceneProc *_sceneProc;
SceneImage _bg;
SceneImage _bgMask;
@@ -456,8 +435,8 @@ class Scene {
static int SC_ITEIntroFaireTentProc(int param, void *refCon);
private:
- Event *ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialogue dialogue[]);
- Event *ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]);
+ EventColumns *ITEQueueDialogue(EventColumns *eventColumns, int n_dialogues, const IntroDialogue dialogue[]);
+ EventColumns *ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]);
int ITEIntroAnimProc(int param);
int ITEIntroCave1Proc(int param);
int ITEIntroCave2Proc(int param);
diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp
index b0e20da48c..3ae2f19e1b 100644
--- a/engines/saga/script.cpp
+++ b/engines/saga/script.cpp
@@ -46,12 +46,11 @@ namespace Saga {
SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
ResourceContext *resourceContext;
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
int prevTell;
- int i, j;
- byte *stringsPointer;
- size_t stringsLength;
+ uint ui;
+ int j;
+ ByteArray stringsData;
//initialize member variables
_abortEnabled = true;
@@ -67,9 +66,7 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
_pointerObject = ID_NOTHING;
_staticSize = 0;
- _commonBufferSize = COMMON_BUFFER_SIZE;
- _commonBuffer = (byte*)malloc(_commonBufferSize);
- memset(_commonBuffer, 0, _commonBufferSize);
+ _commonBuffer.resize(COMMON_BUFFER_SIZE);
debug(8, "Initializing scripting subsystem");
// Load script resource file context
@@ -86,47 +83,41 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
uint32 scriptResourceId = 0;
scriptResourceId = _vm->getResourceDescription()->moduleLUTResourceId;
debug(3, "Loading module LUT from resource %i", scriptResourceId);
- _vm->_resource->loadResource(resourceContext, scriptResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, scriptResourceId, resourceData);
// Create logical script LUT from resource
- if (resourceLength % 22 == 0) { // ITE CD
+ if (resourceData.size() % 22 == 0) { // ITE CD
_modulesLUTEntryLen = 22;
- } else if (resourceLength % 16 == 0) { // ITE disk, IHNM
+ } else if (resourceData.size() % 16 == 0) { // ITE disk, IHNM
_modulesLUTEntryLen = 16;
} else {
- error("Script::Script() Invalid script lookup table length (%i)", (int)resourceLength);
+ error("Script::Script() Invalid script lookup table length (%i)", (int)resourceData.size());
}
// Calculate number of entries
- _modulesCount = resourceLength / _modulesLUTEntryLen;
+ int modulesCount = resourceData.size() / _modulesLUTEntryLen;
- debug(3, "LUT has %i entries", _modulesCount);
+ debug(3, "LUT has %i entries", modulesCount);
// Allocate space for logical LUT
- _modules = (ModuleData *)malloc(_modulesCount * sizeof(*_modules));
- if (_modules == NULL) {
- memoryError("Script::Script()");
- }
+ _modules.resize(modulesCount);
// Convert LUT resource to logical LUT
- MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, resourceContext->isBigEndian());
- for (i = 0; i < _modulesCount; i++) {
- memset(&_modules[i], 0, sizeof(ModuleData));
+ ByteArrayReadStreamEndian scriptS(resourceData, resourceContext->isBigEndian());
+ for (ui = 0; ui < _modules.size(); ui++) {
prevTell = scriptS.pos();
- _modules[i].scriptResourceId = scriptS.readUint16();
- _modules[i].stringsResourceId = scriptS.readUint16();
- _modules[i].voicesResourceId = scriptS.readUint16();
+ _modules[ui].scriptResourceId = scriptS.readUint16();
+ _modules[ui].stringsResourceId = scriptS.readUint16();
+ _modules[ui].voicesResourceId = scriptS.readUint16();
// Skip the unused portion of the structure
for (j = scriptS.pos(); j < prevTell + _modulesLUTEntryLen; j++) {
if (scriptS.readByte() != 0)
- warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", i, j);
+ warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", ui, j);
}
}
- free(resourcePointer);
-
// TODO
//
// In ITE, the "main strings" resource contains both the verb strings
@@ -135,10 +126,9 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
// In IHNM, the "main strings" contains the verb strings, but not the
// object names. At least, I think that's the case.
- _vm->_resource->loadResource(resourceContext, _vm->getResourceDescription()->mainStringsResourceId, stringsPointer, stringsLength);
+ _vm->_resource->loadResource(resourceContext, _vm->getResourceDescription()->mainStringsResourceId, stringsData);
- _vm->loadStrings(_mainStrings, stringsPointer, stringsLength);
- free(stringsPointer);
+ _vm->loadStrings(_mainStrings, stringsData);
setupScriptOpcodeList();
@@ -157,19 +147,10 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
SAGA1Script::~SAGA1Script() {
debug(8, "Shutting down scripting subsystem.");
-
- _mainStrings.freeMem();
- _globalVoiceLUT.freeMem();
-
- freeModules();
- free(_modules);
-
- free(_commonBuffer);
}
SAGA2Script::SAGA2Script(SagaEngine *vm) : Script(vm) {
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
debug(8, "Initializing scripting subsystem");
// Load script resource file context
@@ -184,14 +165,14 @@ SAGA2Script::SAGA2Script(SagaEngine *vm) : Script(vm) {
if (entryNum < 0)
error("Unable to locate the script's export segment");
debug(3, "Loading module LUT from resource %i", entryNum);
- _vm->_resource->loadResource(_scriptContext, (uint32)entryNum, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, (uint32)entryNum, resourceData);
_modulesLUTEntryLen = sizeof(uint32);
// Calculate number of entries
- _modulesCount = resourceLength / _modulesLUTEntryLen + 1;
+ int modulesCount = resourceData.size() / _modulesLUTEntryLen + 1;
- debug(3, "LUT has %i entries", _modulesCount);
+ debug(3, "LUT has %i entries", modulesCount);
// Script data segment
/*
@@ -994,8 +975,8 @@ void Script::opSpeak(SCRIPTOP_PARAMS) {
}
} else {
#endif
- if (thread->_voiceLUT->voicesCount > first)
- sampleResourceId = thread->_voiceLUT->voices[first];
+ if (thread->_voiceLUT->size() > uint16(first))
+ sampleResourceId = (*thread->_voiceLUT)[uint16(first)];
#if 0
}
#endif
@@ -1067,12 +1048,11 @@ void Script::opJmpSeedRandom(SCRIPTOP_PARAMS) {
warning("opJmpSeedRandom");
}
-void Script::loadModule(int scriptModuleNumber) {
- byte *resourcePointer;
- size_t resourceLength;
+void Script::loadModule(uint scriptModuleNumber) {
+ ByteArray resourceData;
// Validate script number
- if ((scriptModuleNumber < 0) || (scriptModuleNumber >= _modulesCount)) {
+ if (scriptModuleNumber >= _modules.size()) {
error("Script::loadScript() Invalid script module number");
}
@@ -1083,79 +1063,70 @@ void Script::loadModule(int scriptModuleNumber) {
// Initialize script data structure
debug(3, "Loading script module #%d", scriptModuleNumber);
- _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].scriptResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].scriptResourceId, resourceData);
- loadModuleBase(_modules[scriptModuleNumber], resourcePointer, resourceLength);
- free(resourcePointer);
+ loadModuleBase(_modules[scriptModuleNumber], resourceData);
- _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].stringsResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].stringsResourceId, resourceData);
- _vm->loadStrings(_modules[scriptModuleNumber].strings, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->loadStrings(_modules[scriptModuleNumber].strings, resourceData);
if (_modules[scriptModuleNumber].voicesResourceId > 0) {
- _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].voicesResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].voicesResourceId, resourceData);
- loadVoiceLUT(_modules[scriptModuleNumber].voiceLUT, resourcePointer, resourceLength);
- free(resourcePointer);
+ loadVoiceLUT(_modules[scriptModuleNumber].voiceLUT, resourceData);
}
_modules[scriptModuleNumber].staticOffset = _staticSize;
_staticSize += _modules[scriptModuleNumber].staticSize;
- if (_staticSize > _commonBufferSize) {
- error("Script::loadModule() _staticSize > _commonBufferSize");
+ if (_staticSize > _commonBuffer.size()) {
+ error("Script::loadModule() _staticSize > _commonBuffer.size()");
}
_modules[scriptModuleNumber].loaded = true;
}
-void Script::freeModules() {
- int i;
- for (i = 0; i < _modulesCount; i++) {
+void Script::clearModules() {
+ uint i;
+ for (i = 0; i < _modules.size(); i++) {
if (_modules[i].loaded) {
- _modules[i].freeMem();
- _modules[i].loaded = false;
+ _modules[i].clear();
}
}
_staticSize = 0;
}
-void Script::loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength) {
- int i;
+void Script::loadModuleBase(ModuleData &module, const ByteArray &resourceData) {
+ uint i;
debug(3, "Loading module base...");
- module.moduleBase = (byte*)malloc(resourceLength);
- module.moduleBaseSize = resourceLength;
-
- memcpy(module.moduleBase, resourcePointer, resourceLength);
+ module.moduleBase.assign(resourceData);
- MemoryReadStreamEndian scriptS(module.moduleBase, module.moduleBaseSize, _scriptContext->isBigEndian());
+ ByteArrayReadStreamEndian scriptS(module.moduleBase, _scriptContext->isBigEndian());
- module.entryPointsCount = scriptS.readUint16();
+ uint entryPointsCount = scriptS.readUint16();
scriptS.readUint16(); //skip
- module.entryPointsTableOffset = scriptS.readUint16();
+ uint16 entryPointsTableOffset; // offset of entrypoint table in moduleBase
+ entryPointsTableOffset = scriptS.readUint16();
scriptS.readUint16(); //skip
- if ((module.moduleBaseSize - module.entryPointsTableOffset) < (module.entryPointsCount * SCRIPT_TBLENTRY_LEN)) {
+ if ((module.moduleBase.size() - entryPointsTableOffset) < (entryPointsCount * SCRIPT_TBLENTRY_LEN)) {
error("Script::loadModuleBase() Invalid table offset");
}
- if (module.entryPointsCount > SCRIPT_MAX) {
+ if (entryPointsCount > SCRIPT_MAX) {
error("Script::loadModuleBase()Script limit exceeded");
}
- module.entryPoints = (EntryPoint *)malloc(module.entryPointsCount * sizeof(*module.entryPoints));
- if (module.entryPoints == NULL) {
- memoryError("Script::loadModuleBase");
- }
+ module.entryPoints.resize(entryPointsCount);
// Read in the entrypoint table
module.staticSize = scriptS.readUint16();
- while (scriptS.pos() < module.entryPointsTableOffset)
+ while (scriptS.pos() < entryPointsTableOffset)
scriptS.readByte();
- for (i = 0; i < module.entryPointsCount; i++) {
+ for (i = 0; i < module.entryPoints.size(); i++) {
// First uint16 is the offset of the entrypoint name from the start
// of the bytecode resource, second uint16 is the offset of the
// bytecode itself for said entrypoint
@@ -1163,26 +1134,21 @@ void Script::loadModuleBase(ModuleData &module, const byte *resourcePointer, siz
module.entryPoints[i].offset = scriptS.readUint16();
// Perform a simple range check on offset values
- if ((module.entryPoints[i].nameOffset >= module.moduleBaseSize) || (module.entryPoints[i].offset >= module.moduleBaseSize)) {
+ if ((module.entryPoints[i].nameOffset >= module.moduleBase.size()) || (module.entryPoints[i].offset >= module.moduleBase.size())) {
error("Script::loadModuleBase() Invalid offset encountered in script entrypoint table");
}
}
}
-void Script::loadVoiceLUT(VoiceLUT &voiceLUT, const byte *resourcePointer, size_t resourceLength) {
+void Script::loadVoiceLUT(VoiceLUT &voiceLUT, const ByteArray &resourceData) {
uint16 i;
- voiceLUT.voicesCount = resourceLength / 2;
-
- voiceLUT.voices = (uint16 *)malloc(voiceLUT.voicesCount * sizeof(*voiceLUT.voices));
- if (voiceLUT.voices == NULL) {
- error("Script::loadVoiceLUT() not enough memory");
- }
+ voiceLUT.resize(resourceData.size() / 2);
- MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, _scriptContext->isBigEndian());
+ ByteArrayReadStreamEndian scriptS(resourceData, _scriptContext->isBigEndian());
- for (i = 0; i < voiceLUT.voicesCount; i++) {
- voiceLUT.voices[i] = scriptS.readUint16();
+ for (i = 0; i < voiceLUT.size(); i++) {
+ voiceLUT[i] = scriptS.readUint16();
}
}
@@ -1442,7 +1408,7 @@ void Script::doVerb() {
event.param4 = _pendingObject[0]; // Object
event.param5 = _pendingObject[1]; // With Object
event.param6 = (objectType == kGameObjectActor) ? _pendingObject[0] : ID_PROTAG; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
} else {
// Show excuse text in ITE CD Versions
diff --git a/engines/saga/script.h b/engines/saga/script.h
index 21afeb5c44..080b1558d6 100644
--- a/engines/saga/script.h
+++ b/engines/saga/script.h
@@ -129,16 +129,7 @@ struct EntryPoint {
uint16 offset;
};
-struct VoiceLUT {
- uint16 voicesCount;
- uint16 *voices;
- void freeMem() {
- voicesCount = 0;
- free(voices);
- }
- VoiceLUT() {
- memset(this, 0, sizeof(*this));
- }
+class VoiceLUT : public Common::Array<uint16> {
};
struct ModuleData {
@@ -147,28 +138,29 @@ struct ModuleData {
int stringsResourceId;
int voicesResourceId;
- byte *moduleBase; // all base module
- uint16 moduleBaseSize; // base module size
+ ByteArray moduleBase; // all base module
uint16 staticSize; // size of static data
uint staticOffset; // offset of static data begining in _commonBuffer
-
- uint16 entryPointsTableOffset; // offset of entrypoint table in moduleBase
- uint16 entryPointsCount;
- EntryPoint *entryPoints;
+ Common::Array<EntryPoint> entryPoints;
StringsTable strings;
VoiceLUT voiceLUT;
- void freeMem() {
- strings.freeMem();
- voiceLUT.freeMem();
- free(moduleBase);
- free(entryPoints);
+
+ void clear() {
+ loaded = false;
+ strings.clear();
+ voiceLUT.clear();
+ moduleBase.clear();
+ entryPoints.clear();
+ }
+
+ ModuleData() : loaded(false), scriptResourceId(0), stringsResourceId(0), voicesResourceId(0), staticSize(0), staticOffset(0) {
}
};
class ScriptThread {
public:
- int16 *_stackBuf;
+ Common::Array<int16> _stackBuf;
uint16 _stackTopIndex;
uint16 _frameIndex;
@@ -264,47 +256,21 @@ public:
}
ScriptThread() {
- memset(this, 0xFE, sizeof(*this));
- _flags = kTFlagNone;
- _stackBuf = 0;
- }
-
- // copy constructor
- ScriptThread(const ScriptThread& s) {
- // Verify that s doesn't have a non-zero _stackBuf, for else
- // we would have to clone that buffer, too, which we currently
- // don't do. This case should never occur anyway, though (at
- // least as long as the thread handling code does not change).
- assert(!s._stackBuf);
+ memset(&_frameIndex, 0xFE, sizeof(_frameIndex));
+ memset(_threadVars, 0xFE, sizeof(_threadVars));
+ memset(&_waitType, 0xFE, sizeof(_waitType));
+ memset(&_sleepTime, 0xFE, sizeof(_sleepTime));
+ memset(&_threadObj, 0xFE, sizeof(_threadObj));
+ memset(&_returnValue, 0xFE, sizeof(_threadObj));
+ memset(&_frameWait, 0xFE, sizeof(_frameWait));
- memcpy(this, &s, sizeof(*this));
- }
-
- // assignment operator
- ScriptThread& operator=(const ScriptThread &s) {
- if (this == &s)
- return *this;
-
- // Verify that s doesn't have a non-zero _stackBuf, for else
- // we would have to clone that buffer, too, which we currently
- // don't do. This case should never occur anyway, though (at
- // least as long as the thread handling code does not change).
- assert(!s._stackBuf);
-
- free(_stackBuf);
- memcpy(this, &s, sizeof(*this));
-
- return *this;
- }
-
- ~ScriptThread() {
- free(_stackBuf);
+ _flags = kTFlagNone;
}
};
typedef Common::List<ScriptThread> ScriptThreadList;
-#define SCRIPTOP_PARAMS ScriptThread *thread, MemoryReadStream *scriptS, bool &stopParsing, bool &breakOut
+#define SCRIPTOP_PARAMS ScriptThread *thread, Common::SeekableReadStream *scriptS, bool &stopParsing, bool &breakOut
#define SCRIPTFUNC_PARAMS ScriptThread *thread, int nArgs, bool &disContinue
#define OPCODE(x) {&Script::x, #x}
@@ -315,8 +281,8 @@ public:
Script(SagaEngine *vm);
virtual ~Script();
- void loadModule(int scriptModuleNumber);
- void freeModules();
+ void loadModule(uint scriptModuleNumber);
+ void clearModules();
void doVerb();
void showVerb(int statusColor = -1);
@@ -384,13 +350,11 @@ protected:
ResourceContext *_dataContext;
uint16 _modulesLUTEntryLen;
- ModuleData *_modules;
- int _modulesCount;
+ Common::Array<ModuleData> _modules;
TextListEntry *_placardTextEntry;
friend class SagaEngine;
- byte *_commonBuffer;
- uint _commonBufferSize;
+ ByteArray _commonBuffer;
uint _staticSize;
ScriptThreadList _threadList;
@@ -428,10 +392,10 @@ public:
void wakeUpThreads(int waitType);
void wakeUpThreadsDelayed(int waitType, int sleepTime);
- void loadVoiceLUT(VoiceLUT &voiceLUT, const byte *resourcePointer, size_t resourceLength);
+ void loadVoiceLUT(VoiceLUT &voiceLUT, const ByteArray &resourceData);
protected:
- void loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength);
+ void loadModuleBase(ModuleData &module, const ByteArray &resourceData);
// runThread returns true if we should break running of other threads
bool runThread(ScriptThread &thread);
diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp
index 328d4040af..1e34362dc4 100644
--- a/engines/saga/sfuncs.cpp
+++ b/engines/saga/sfuncs.cpp
@@ -327,7 +327,7 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) {
event.param4 = theObject; // Object
event.param5 = withObject; // With Object
event.param6 = objectId;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
// Script function #8 (0x08) nonblocking
@@ -782,11 +782,11 @@ void Script::sfSimulSpeech(SCRIPTFUNC_PARAMS) {
for (i = 0; i < actorsCount; i++)
actorsIds[i] = thread->pop();
- if (thread->_voiceLUT->voices) {
+ if (!thread->_voiceLUT->empty()) {
if (_vm->getGameId() == GID_IHNM && stringId >= 338) {
sampleResourceId = -1;
} else {
- sampleResourceId = thread->_voiceLUT->voices[stringId];
+ sampleResourceId = (*thread->_voiceLUT)[stringId];
if (sampleResourceId <= 0 || sampleResourceId > 4000)
sampleResourceId = -1;
}
@@ -953,7 +953,7 @@ void Script::sfPlaceActor(SCRIPTFUNC_PARAMS) {
int frameOffset = thread->pop();
ActorFrameRange *frameRange;
- debug(1, "sfPlaceActor(id = 0x%x, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actor->_location.x,
+ debug(1, "sfPlaceActor(id = 0x%X, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actor->_location.x,
actor->_location.y, actor->_facingDirection, frameType, frameOffset);
if (frameType >= 0) {
@@ -1042,8 +1042,8 @@ void Script::sfSimulSpeech2(SCRIPTFUNC_PARAMS) {
for (i = 0; i < actorsCount; i++)
actorsIds[i] = thread->pop();
- if (thread->_voiceLUT->voices) {
- sampleResourceId = thread->_voiceLUT->voices[stringId];
+ if (!thread->_voiceLUT->empty()) {
+ sampleResourceId = (*thread->_voiceLUT)[stringId];
if (sampleResourceId <= 0 || sampleResourceId > 4000)
sampleResourceId = -1;
}
@@ -1060,7 +1060,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
static PalEntry cur_pal[PAL_ENTRIES];
PalEntry *pal;
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
thread->wait(kWaitTypePlacard);
@@ -1070,7 +1070,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
_vm->_interface->setFadeMode(kFadeOut);
@@ -1082,7 +1082,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1091,12 +1091,12 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kInterfaceEvent;
event.op = kEventClearStatus;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kGraphicsEvent;
@@ -1106,7 +1106,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.param3 = _vm->_scene->getHeight();
event.param4 = 0;
event.param5 = _vm->getDisplayInfo().width;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Put the text in the center of the viewport, assuming it will fit on
// one line. If we cannot make that assumption we'll need to extend
@@ -1130,7 +1130,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.code = kTextEvent;
event.op = kEventDisplay;
event.data = _placardTextEntry;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
_vm->_scene->getBGPal(pal);
event.type = kEvTImmediate;
@@ -1139,13 +1139,13 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypePlacard;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
@@ -1349,8 +1349,8 @@ void Script::sfPlayMusic(SCRIPTFUNC_PARAMS) {
return;
}
- if (param1 >= _vm->_music->_songTableLen) {
- warning("sfPlayMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTableLen - 1);
+ if (uint(param1) >= _vm->_music->_songTable.size()) {
+ warning("sfPlayMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTable.size() - 1);
} else {
_vm->_music->setVolume(_vm->_musicVolume, 1);
_vm->_music->play(_vm->_music->_songTable[param1], param2 ? MUSIC_LOOP : MUSIC_NORMAL);
@@ -1440,7 +1440,7 @@ void Script::sfPlaySound(SCRIPTFUNC_PARAMS) {
int16 param = thread->pop();
int res;
- if (param >= 0 && param < _vm->_sndRes->_fxTableLen) {
+ if (uint(param) < _vm->_sndRes->_fxTable.size()) {
res = _vm->_sndRes->_fxTable[param].res;
if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
res -= 14;
@@ -1455,7 +1455,7 @@ void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) {
int16 param = thread->pop();
int res;
- if (param >= 0 && param < _vm->_sndRes->_fxTableLen) {
+ if (uint(param) < _vm->_sndRes->_fxTable.size()) {
res = _vm->_sndRes->_fxTable[param].res;
if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
res -= 14;
@@ -1527,7 +1527,7 @@ void Script::finishDialog(int strID, int replyID, int flags, int bitOffset) {
const char *str = _conversingThread->_strings->getString(strID);
if (*str != '[') {
int sampleResourceId = -1;
- sampleResourceId = _conversingThread->_voiceLUT->voices[strID];
+ sampleResourceId = (*_conversingThread->_voiceLUT)[strID];
if (sampleResourceId < 0 || sampleResourceId > 4000)
sampleResourceId = -1;
diff --git a/engines/saga/sfuncs_ihnm.cpp b/engines/saga/sfuncs_ihnm.cpp
index b98c1cb852..dd6bbbe6f8 100644
--- a/engines/saga/sfuncs_ihnm.cpp
+++ b/engines/saga/sfuncs_ihnm.cpp
@@ -247,7 +247,7 @@ void Script::sfScriptFade(SCRIPTFUNC_PARAMS) {
event.param2 = endingBrightness;
event.param3 = firstPalEntry;
event.param4 = lastPalEntry - firstPalEntry + 1;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
void Script::sfScriptStartVideo(SCRIPTFUNC_PARAMS) {
@@ -294,7 +294,7 @@ void Script::sfAddIHNMDemoHelpTextLine(SCRIPTFUNC_PARAMS) {
event.code = kTextEvent;
event.op = kEventDisplay;
event.data = _psychicProfileTextEntry;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_ihnmDemoCurrentY += _vm->_font->getHeight(kKnownFontVerb, thread->_strings->getString(stringId), 226, kFontCentered);
}
@@ -413,8 +413,8 @@ void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) {
return;
}
- if (param1 >= _vm->_music->_songTableLen) {
- warning("sfQueueMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTableLen - 1);
+ if (uint(param1) >= _vm->_music->_songTable.size()) {
+ warning("sfQueueMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTable.size() - 1);
} else {
_vm->_music->setVolume(_vm->_musicVolume, 1);
event.type = kEvTOneshot;
@@ -424,7 +424,7 @@ void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) {
event.op = kEventPlay;
event.time = _vm->ticksToMSec(1000);
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
if (!_vm->_scene->haveChapterPointsChanged()) {
_vm->_scene->setCurrentMusicTrack(param1);
diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp
index 2137423a5a..592c2d0618 100644
--- a/engines/saga/shorten.cpp
+++ b/engines/saga/shorten.cpp
@@ -239,7 +239,7 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
return NULL;
}
- // Get block size
+ // Get block size
if (version > 0) {
blockSize = gReader->getUint32((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2));
maxLPC = gReader->getUint32(2);
@@ -366,7 +366,12 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
if (maxLPC < lpcNum) {
warning("Safeguard: maxLPC < lpcNum (should never happen)");
maxLPC = lpcNum;
- lpc = (int32 *) realloc(lpc, maxLPC * 4);
+ int32 *tmp = (int32 *) realloc(lpc, maxLPC * 4);
+ if ((tmp != NULL) || (maxLPC == 0)) {
+ lpc = tmp;
+ } else {
+ error("loadShortenFromStream(): Error while reallocating memory");
+ }
}
for (i = 0; i < lpcNum; i++)
@@ -430,7 +435,12 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
prevSize = size;
size += (blockSize * dataSize);
- unpackedBuffer = (byte *) realloc(unpackedBuffer, size);
+ byte *tmp = (byte *) realloc(unpackedBuffer, size);
+ if ((tmp != NULL) || (size == 0)) {
+ unpackedBuffer = tmp;
+ } else {
+ error("loadShortenFromStream(): Error while reallocating memory");
+ }
pBuf = unpackedBuffer + prevSize;
if (flags & Audio::FLAG_16BITS) {
@@ -464,7 +474,12 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
uint32 vLen = (uint32)gReader->getURice(5);
prevSize = size;
size += vLen;
- unpackedBuffer = (byte *) realloc(unpackedBuffer, size);
+ byte *tmp = (byte *) realloc(unpackedBuffer, size);
+ if ((tmp != NULL) || (size == 0)) {
+ unpackedBuffer = tmp;
+ } else {
+ error("loadShortenFromStream(): Error while reallocating memory");
+ }
pBuf = unpackedBuffer + prevSize;
while (vLen--) {
diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp
index 9322918db5..74fde3e497 100644
--- a/engines/saga/sndres.cpp
+++ b/engines/saga/sndres.cpp
@@ -60,8 +60,11 @@ SndRes::SndRes(SagaEngine *vm) : _vm(vm), _sfxContext(NULL), _voiceContext(NULL)
setVoiceBank(0);
if (_vm->getGameId() == GID_ITE) {
- _fxTable = ITE_SfxTable;
- _fxTableLen = ITE_SFXCOUNT;
+ _fxTable.resize(ITE_SFXCOUNT);
+ for (uint i = 0; i < _fxTable.size(); i++) {
+ _fxTable[i].res = ITE_SfxTable[i].res;
+ _fxTable[i].vol = ITE_SfxTable[i].vol;
+ }
#ifdef ENABLE_IHNM
} else if (_vm->getGameId() == GID_IHNM) {
ResourceContext *resourceContext;
@@ -71,32 +74,24 @@ SndRes::SndRes(SagaEngine *vm) : _vm(vm), _sfxContext(NULL), _voiceContext(NULL)
error("Resource::loadGlobalResources() resource context not found");
}
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
if (_vm->isIHNMDemo()) {
- _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_SFX_LUT,
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_SFX_LUT, resourceData);
} else {
- _vm->_resource->loadResource(resourceContext, RID_IHNM_SFX_LUT,
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_SFX_LUT, resourceData);
}
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Sndres::SndRes can't read SfxIDs table");
}
- _fxTableIDsLen = resourceLength / 2;
- _fxTableIDs = (int16 *)malloc(_fxTableIDsLen * sizeof(int16));
+ _fxTableIDs.resize(resourceData.size() / 2);
- MemoryReadStream metaS(resourcePointer, resourceLength);
- for (int i = 0; i < _fxTableIDsLen; i++)
+ ByteArrayReadStreamEndian metaS(resourceData);
+ for (uint i = 0; i < _fxTableIDs.size(); i++) {
_fxTableIDs[i] = metaS.readSint16LE();
-
- free(resourcePointer);
-
- _fxTable = 0;
- _fxTableLen = 0;
+ }
#endif
#ifdef ENABLE_SAGA2
} else if (_vm->getGameId() == GID_DINO) {
@@ -108,12 +103,6 @@ SndRes::SndRes(SagaEngine *vm) : _vm(vm), _sfxContext(NULL), _voiceContext(NULL)
}
SndRes::~SndRes() {
-#ifdef ENABLE_IHNM
- if (_vm->getGameId() == GID_IHNM) {
- free(_fxTable);
- free(_fxTableIDs);
- }
-#endif
}
void SndRes::setVoiceBank(int serial) {
@@ -327,7 +316,7 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
#endif
} else if (resourceType == kSoundVOC) {
data = Audio::loadVOCFromStream(readS, size, rate);
- result = (data != 0);
+ result = (data != NULL);
if (onlyHeader)
free(data);
buffer.flags |= Audio::FLAG_UNSIGNED;
@@ -339,11 +328,13 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
buffer.frequency = rate;
buffer.size = size;
- if (!onlyHeader && resourceType != kSoundVOC) {
- buffer.buffer = (byte *)malloc(size);
- readS.read(buffer.buffer, size);
- } else if (!onlyHeader && resourceType == kSoundVOC) {
- buffer.buffer = data;
+ if (!onlyHeader) {
+ if (resourceType == kSoundVOC) {
+ buffer.buffer = data;
+ } else {
+ buffer.buffer = (byte *)malloc(size);
+ readS.read(buffer.buffer, size);
+ }
}
}
break;
diff --git a/engines/saga/sndres.h b/engines/saga/sndres.h
index d5507ebc55..e4bae1b143 100644
--- a/engines/saga/sndres.h
+++ b/engines/saga/sndres.h
@@ -33,6 +33,11 @@
namespace Saga {
+struct FxTable {
+ int16 res;
+ int16 vol;
+};
+
class SndRes {
public:
@@ -44,11 +49,9 @@ public:
int getVoiceLength(uint32 resourceId);
void setVoiceBank(int serial);
- FxTable *_fxTable;
- int _fxTableLen;
+ Common::Array<FxTable> _fxTable;
- int16 *_fxTableIDs;
- int _fxTableIDsLen;
+ Common::Array<int16> _fxTableIDs;
private:
bool load(ResourceContext *context, uint32 resourceId, SoundBuffer &buffer, bool onlyHeader);
diff --git a/engines/saga/sound.cpp b/engines/saga/sound.cpp
index db979e8104..b3fcf4def3 100644
--- a/engines/saga/sound.cpp
+++ b/engines/saga/sound.cpp
@@ -36,7 +36,7 @@
namespace Saga {
Sound::Sound(SagaEngine *vm, Audio::Mixer *mixer) :
- _vm(vm), _mixer(mixer), _voxStream(0) {
+ _vm(vm), _mixer(mixer) {
for (int i = 0; i < SOUND_HANDLES; i++)
_handles[i].type = kFreeHandle;
@@ -45,7 +45,6 @@ Sound::Sound(SagaEngine *vm, Audio::Mixer *mixer) :
}
Sound::~Sound() {
- delete _voxStream;
}
SndHandle *Sound::getHandle() {
diff --git a/engines/saga/sound.h b/engines/saga/sound.h
index 7ee2765a0f..b5f82369f4 100644
--- a/engines/saga/sound.h
+++ b/engines/saga/sound.h
@@ -95,7 +95,6 @@ public:
SagaEngine *_vm;
Audio::Mixer *_mixer;
- MemoryReadStream *_voxStream;
SndHandle _handles[SOUND_HANDLES];
};
diff --git a/engines/saga/sprite.cpp b/engines/saga/sprite.cpp
index c1a9846b47..bc3646e898 100644
--- a/engines/saga/sprite.cpp
+++ b/engines/saga/sprite.cpp
@@ -51,13 +51,6 @@ Sprite::Sprite(SagaEngine *vm) : _vm(vm) {
error("Sprite::Sprite resource context not found");
}
- _decodeBufLen = DECODE_BUF_LEN;
-
- _decodeBuf = (byte *)malloc(_decodeBufLen);
- if (_decodeBuf == NULL) {
- memoryError("Sprite::Sprite");
- }
-
if (_vm->getGameId() == GID_ITE) {
loadList(_vm->getResourceDescription()->mainSpritesResourceId, _mainSprites);
_arrowSprites = _saveReminderSprites = _inventorySprites = _mainSprites;
@@ -78,71 +71,58 @@ Sprite::Sprite(SagaEngine *vm) : _vm(vm) {
Sprite::~Sprite() {
debug(8, "Shutting down sprite subsystem...");
- _mainSprites.freeMem();
- if (_vm->getGameId() == GID_IHNM) {
- _inventorySprites.freeMem();
- _arrowSprites.freeMem();
- _saveReminderSprites.freeMem();
- }
- free(_decodeBuf);
}
void Sprite::loadList(int resourceId, SpriteList &spriteList) {
SpriteInfo *spriteInfo;
- byte *spriteListData = 0;
- size_t spriteListLength = 0;
+ ByteArray spriteListData;
uint16 oldSpriteCount;
uint16 newSpriteCount;
uint16 spriteCount;
- int i;
+ uint i;
int outputLength, inputLength;
uint32 offset;
const byte *spritePointer;
const byte *spriteDataPointer;
- _vm->_resource->loadResource(_spriteContext, resourceId, spriteListData, spriteListLength);
+ _vm->_resource->loadResource(_spriteContext, resourceId, spriteListData);
- if (spriteListLength == 0) {
+ if (spriteListData.empty()) {
return;
}
- MemoryReadStreamEndian readS(spriteListData, spriteListLength, _spriteContext->isBigEndian());
+ ByteArrayReadStreamEndian readS(spriteListData, _spriteContext->isBigEndian());
spriteCount = readS.readUint16();
debug(9, "Sprites: %d", spriteCount);
- oldSpriteCount = spriteList.spriteCount;
- newSpriteCount = spriteList.spriteCount + spriteCount;
+ oldSpriteCount = spriteList.size();
+ newSpriteCount = oldSpriteCount + spriteCount;
- spriteList.infoList = (SpriteInfo *)realloc(spriteList.infoList, newSpriteCount * sizeof(*spriteList.infoList));
- if (spriteList.infoList == NULL) {
- memoryError("Sprite::loadList");
- }
-
- spriteList.spriteCount = newSpriteCount;
+ spriteList.resize(newSpriteCount);
bool bigHeader = _vm->getGameId() == GID_IHNM || _vm->isMacResources();
- for (i = oldSpriteCount; i < spriteList.spriteCount; i++) {
- spriteInfo = &spriteList.infoList[i];
+ for (i = oldSpriteCount; i < spriteList.size(); i++) {
+ spriteInfo = &spriteList[i];
if (bigHeader)
offset = readS.readUint32();
else
offset = readS.readUint16();
- if (offset >= spriteListLength) {
+ if (offset >= spriteListData.size()) {
// ITE Mac demos throw this warning
warning("Sprite::loadList offset exceeded");
- spriteList.spriteCount = i;
+ spriteList.resize(i);
return;
}
- spritePointer = spriteListData;
+ spritePointer = spriteListData.getBuffer();
spritePointer += offset;
if (bigHeader) {
- MemoryReadStreamEndian readS2(spritePointer, 8, _spriteContext->isBigEndian());
+ Common::MemoryReadStreamEndian readS2(spritePointer, 8, _spriteContext->isBigEndian());
spriteInfo->xAlign = readS2.readSint16();
spriteInfo->yAlign = readS2.readSint16();
@@ -152,7 +132,7 @@ void Sprite::loadList(int resourceId, SpriteList &spriteList) {
spriteDataPointer = spritePointer + readS2.pos();
} else {
- MemoryReadStreamEndian readS2(spritePointer, 4);
+ Common::MemoryReadStreamEndian readS2(spritePointer, 4, false);
spriteInfo->xAlign = readS2.readSByte();
spriteInfo->yAlign = readS2.readSByte();
@@ -163,114 +143,147 @@ void Sprite::loadList(int resourceId, SpriteList &spriteList) {
}
outputLength = spriteInfo->width * spriteInfo->height;
- inputLength = spriteListLength - (spriteDataPointer - spriteListData);
- decodeRLEBuffer(spriteDataPointer, inputLength, outputLength);
- spriteInfo->decodedBuffer = (byte *) malloc(outputLength);
- if (spriteInfo->decodedBuffer == NULL) {
- memoryError("Sprite::loadList");
- }
-
+ inputLength = spriteListData.size() - (spriteDataPointer - spriteListData.getBuffer());
+ spriteInfo->decodedBuffer.resize(outputLength);
+ if (outputLength > 0) {
+ decodeRLEBuffer(spriteDataPointer, inputLength, outputLength);
+ byte *dst = &spriteInfo->decodedBuffer.front();
#ifdef ENABLE_IHNM
- // IHNM sprites are upside-down, for reasons which i can only
- // assume are perverse. To simplify things, flip them now. Not
- // at drawing time.
-
- if (_vm->getGameId() == GID_IHNM) {
- byte *src = _decodeBuf + spriteInfo->width * (spriteInfo->height - 1);
- byte *dst = spriteInfo->decodedBuffer;
-
- for (int j = 0; j < spriteInfo->height; j++) {
- memcpy(dst, src, spriteInfo->width);
- src -= spriteInfo->width;
- dst += spriteInfo->width;
- }
- } else
+ // IHNM sprites are upside-down, for reasons which i can only
+ // assume are perverse. To simplify things, flip them now. Not
+ // at drawing time.
+
+ if (_vm->getGameId() == GID_IHNM) {
+ byte *src = &_decodeBuf[spriteInfo->width * (spriteInfo->height - 1)];
+
+ for (int j = 0; j < spriteInfo->height; j++) {
+ memcpy(dst, src, spriteInfo->width);
+ src -= spriteInfo->width;
+ dst += spriteInfo->width;
+ }
+ } else
#endif
- memcpy(spriteInfo->decodedBuffer, _decodeBuf, outputLength);
+ memcpy(dst, &_decodeBuf.front(), outputLength);
+ }
}
-
- free(spriteListData);
}
-void Sprite::getScaledSpriteBuffer(SpriteList &spriteList, int spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer) {
+void Sprite::getScaledSpriteBuffer(SpriteList &spriteList, uint spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer) {
SpriteInfo *spriteInfo;
- if (spriteList.spriteCount <= spriteNumber) {
+ if (spriteList.size() <= spriteNumber) {
// this can occur in IHNM while loading a saved game from chapter 1-5 when being in the end chapter
- warning("spriteList.spriteCount <= spriteNumber");
+ warning("spriteList.size() <= spriteNumber");
return;
}
- spriteInfo = &spriteList.infoList[spriteNumber];
+ spriteInfo = &spriteList[spriteNumber];
if (scale < 256) {
- xAlign = (spriteInfo->xAlign * scale) >> 8;
- yAlign = (spriteInfo->yAlign * scale) >> 8;
+ xAlign = (spriteInfo->xAlign * scale) >> 8; //TODO: do we need to take in account sprite x&y aligns ?
+ yAlign = (spriteInfo->yAlign * scale) >> 8; // ????
height = (spriteInfo->height * scale + 0x7f) >> 8;
width = (spriteInfo->width * scale + 0x7f) >> 8;
- scaleBuffer(spriteInfo->decodedBuffer, spriteInfo->width, spriteInfo->height, scale);
- buffer = _decodeBuf;
+ size_t outLength = width * height;
+ if (outLength > 0) {
+ scaleBuffer(&spriteInfo->decodedBuffer.front(), spriteInfo->width, spriteInfo->height, scale, outLength);
+ buffer = &_decodeBuf.front();
+ } else {
+ buffer = NULL;
+ }
} else {
xAlign = spriteInfo->xAlign;
yAlign = spriteInfo->yAlign;
height = spriteInfo->height;
width = spriteInfo->width;
- buffer = spriteInfo->decodedBuffer;
+ buffer = spriteInfo->decodedBuffer.getBuffer();
}
}
void Sprite::drawClip(const Point &spritePointer, int width, int height, const byte *spriteBuffer, bool clipToScene) {
- int clipWidth;
- int clipHeight;
Common::Rect clipRect = clipToScene ? _vm->_scene->getSceneClip() : _vm->getDisplayClip();
- int i, j, jo, io;
+ int xDstOffset, yDstOffset, xSrcOffset, ySrcOffset, xDiff, yDiff, cWidth, cHeight;
byte *bufRowPointer;
+ byte *bufPointer;
const byte *srcRowPointer;
+ const byte *srcPointer;
+
+ int backBufferPitch = _vm->_gfx->getBackBufferPitch();
+
+ //find Rects intersection
+ yDiff = clipRect.top - spritePointer.y;
+ if (yDiff > 0) {
+ ySrcOffset = yDiff;
+ yDstOffset = clipRect.top;
+ cHeight = height - yDiff;
+ } else {
+ ySrcOffset = 0;
+ yDstOffset = spritePointer.y;
+ cHeight = height;
+ }
- bufRowPointer = _vm->_gfx->getBackBufferPixels() + _vm->_gfx->getBackBufferPitch() * spritePointer.y;
- srcRowPointer = spriteBuffer;
-
- clipWidth = CLIP(width, 0, clipRect.right - spritePointer.x);
- clipHeight = CLIP(height, 0, clipRect.bottom - spritePointer.y);
-
- jo = 0;
- io = 0;
- if (spritePointer.x < clipRect.left) {
- jo = clipRect.left - spritePointer.x;
+ xDiff = clipRect.left - spritePointer.x;
+ if (xDiff > 0) {
+ xSrcOffset = xDiff;
+ xDstOffset = clipRect.left;
+ cWidth = width - xDiff;
+ } else {
+ xSrcOffset = 0;
+ xDstOffset = spritePointer.x;
+ cWidth = width;
}
- if (spritePointer.y < clipRect.top) {
- io = clipRect.top - spritePointer.y;
- bufRowPointer += _vm->_gfx->getBackBufferPitch() * io;
- srcRowPointer += width * io;
+
+ yDiff = yDstOffset + cHeight - clipRect.bottom;
+ if (yDiff > 0) {
+ cHeight -= yDiff;
}
- for (i = io; i < clipHeight; i++) {
- for (j = jo; j < clipWidth; j++) {
- assert(_vm->_gfx->getBackBufferPixels() <= (byte *)(bufRowPointer + j + spritePointer.x));
- assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width *
- _vm->getDisplayInfo().height)) > (byte *)(bufRowPointer + j + spritePointer.x));
- assert((const byte *)spriteBuffer <= (const byte *)(srcRowPointer + j));
- assert(((const byte *)spriteBuffer + (width * height)) > (const byte *)(srcRowPointer + j));
+ xDiff = xDstOffset + cWidth - clipRect.right;
+ if (xDiff > 0) {
+ cWidth -= xDiff;
+ }
- if (*(srcRowPointer + j) != 0) {
- *(bufRowPointer + j + spritePointer.x) = *(srcRowPointer + j);
+ if ((cHeight <= 0) || (cWidth <= 0)) {
+ //no intersection
+ return;
+ }
+ bufRowPointer = _vm->_gfx->getBackBufferPixels() + backBufferPitch * yDstOffset + xDstOffset;
+ srcRowPointer = spriteBuffer + width * ySrcOffset + xSrcOffset;
+
+ // validate src, dst buffers
+ assert(_vm->_gfx->getBackBufferPixels() <= bufRowPointer);
+ assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width * _vm->getDisplayInfo().height)) >=
+ (byte *)(bufRowPointer + backBufferPitch * (cHeight - 1) + cWidth));
+ assert((const byte *)spriteBuffer <= srcRowPointer);
+ assert(((const byte *)spriteBuffer + (width * height)) >= (const byte *)(srcRowPointer + width * (cHeight - 1) + cWidth));
+
+ const byte *srcPointerFinish2 = srcRowPointer + width * cHeight;
+ for (;;) {
+ srcPointer = srcRowPointer;
+ bufPointer = bufRowPointer;
+ const byte *srcPointerFinish = srcRowPointer + cWidth;
+ for (;;) {
+ if (*srcPointer != 0) {
+ *bufPointer = *srcPointer;
+ }
+ srcPointer++;
+ bufPointer++;
+ if (srcPointer == srcPointerFinish) {
+ break;
}
}
- bufRowPointer += _vm->_gfx->getBackBufferPitch();
srcRowPointer += width;
+ if (srcRowPointer == srcPointerFinish2) {
+ break;
+ }
+ bufRowPointer += backBufferPitch;
}
- int x1 = MAX<int>(spritePointer.x, 0);
- int y1 = MAX<int>(spritePointer.y, 0);
- int x2 = MIN<int>(MAX<int>(spritePointer.x + clipWidth, 0), clipRect.right);
- int y2 = MIN<int>(MAX<int>(spritePointer.y + clipHeight, 0), clipRect.bottom);
-
- if (x2 > x1 && y2 > y1)
- _vm->_render->addDirtyRect(Common::Rect(x1, y1, x2, y2));
+ _vm->_render->addDirtyRect(Common::Rect(xDstOffset, yDstOffset, xDstOffset + cWidth, yDstOffset + cHeight));
}
-void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Point &screenCoord, int scale, bool clipToScene) {
+void Sprite::draw(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, bool clipToScene) {
const byte *spriteBuffer = NULL;
int width = 0;
int height = 0;
@@ -286,7 +299,7 @@ void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Point &scree
drawClip(spritePointer, width, height, spriteBuffer, clipToScene);
}
-void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Rect &screenRect, int scale, bool clipToScene) {
+void Sprite::draw(SpriteList &spriteList, uint spriteNumber, const Rect &screenRect, int scale, bool clipToScene) {
const byte *spriteBuffer = NULL;
int width = 0;
int height = 0;
@@ -310,7 +323,7 @@ void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Rect &screen
drawClip(spritePointer, width, height, spriteBuffer, clipToScene);
}
-bool Sprite::hitTest(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, const Point &testPoint) {
+bool Sprite::hitTest(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, const Point &testPoint) {
const byte *spriteBuffer = NULL;
int i, j;
const byte *srcRowPointer;
@@ -337,7 +350,7 @@ bool Sprite::hitTest(SpriteList &spriteList, int spriteNumber, const Point &scre
return *srcRowPointer != 0;
}
-void Sprite::drawOccluded(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, int depth) {
+void Sprite::drawOccluded(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, int depth) {
const byte *spriteBuffer = NULL;
int x, y;
byte *destRowPointer;
@@ -356,7 +369,6 @@ void Sprite::drawOccluded(SpriteList &spriteList, int spriteNumber, const Point
int maskWidth;
int maskHeight;
byte *maskBuffer;
- size_t maskBufferLength;
byte *maskRowPointer;
int maskZ;
@@ -365,7 +377,7 @@ void Sprite::drawOccluded(SpriteList &spriteList, int spriteNumber, const Point
return;
}
- _vm->_scene->getBGMaskInfo(maskWidth, maskHeight, maskBuffer, maskBufferLength);
+ _vm->_scene->getBGMaskInfo(maskWidth, maskHeight, maskBuffer);
getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
@@ -420,17 +432,13 @@ void Sprite::decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t ou
byte *outPointerEnd;
int c;
- if (outLength > _decodeBufLen) { // TODO: may we should make dynamic growing?
- error("Sprite::decodeRLEBuffer outLength > _decodeBufLen");
- }
-
- outPointer = _decodeBuf;
- outPointerEnd = _decodeBuf + outLength;
- outPointerEnd--;
+ _decodeBuf.resize(outLength);
+ outPointer = &_decodeBuf.front();
+ outPointerEnd = &_decodeBuf.back();
- memset(outPointer, 0, outLength);
+ memset(outPointer, 0, _decodeBuf.size());
- MemoryReadStream readS(inputBuffer, inLength);
+ Common::MemoryReadStream readS(inputBuffer, inLength);
while (!readS.eos() && (outPointer < outPointerEnd)) {
bg_runcount = readS.readByte();
@@ -458,10 +466,14 @@ void Sprite::decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t ou
}
}
-void Sprite::scaleBuffer(const byte *src, int width, int height, int scale) {
+void Sprite::scaleBuffer(const byte *src, int width, int height, int scale, size_t outLength) {
byte skip = 256 - scale; // skip factor
byte vskip = 0x80, hskip;
- byte *dst = _decodeBuf;
+
+ _decodeBuf.resize(outLength);
+ byte *dst = &_decodeBuf.front();
+
+ memset(dst, 0, _decodeBuf.size());
for (int i = 0; i < height; i++) {
vskip += skip;
diff --git a/engines/saga/sprite.h b/engines/saga/sprite.h
index b7365fd28f..4e463cdd88 100644
--- a/engines/saga/sprite.h
+++ b/engines/saga/sprite.h
@@ -33,32 +33,19 @@ namespace Saga {
#define SPRITE_ZMAX 16
#define SPRITE_ZMASK 0x0F
-#define DECODE_BUF_LEN 64000
-
struct SpriteInfo {
- byte *decodedBuffer;
+ ByteArray decodedBuffer;
int width;
int height;
int xAlign;
int yAlign;
-};
-struct SpriteList {
- int spriteListResourceId;
- int spriteCount;
- SpriteInfo *infoList;
-
- void freeMem() {
- for (int i = 0; i < spriteCount; i++) {
- free(infoList[i].decodedBuffer);
- }
- free(infoList);
- memset(this, 0, sizeof(*this));
+ SpriteInfo() : width(0), height(0), xAlign(0), yAlign(0) {
}
+};
- SpriteList() {
- memset(this, 0, sizeof(*this));
- }
+class SpriteList : public Common::Array<SpriteInfo> {
+// int spriteListResourceId;
};
@@ -73,28 +60,27 @@ public:
~Sprite();
// draw scaled sprite using background scene mask
- void drawOccluded(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, int depth);
+ void drawOccluded(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, int depth);
// draw scaled sprite using background scene mask
- void draw(SpriteList &spriteList, int32 spriteNumber, const Point &screenCoord, int scale, bool clipToScene = false);
+ void draw(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, bool clipToScene = false);
// main function
void drawClip(const Point &spritePointer, int width, int height, const byte *spriteBuffer, bool clipToScene = false);
- void draw(SpriteList &spriteList, int32 spriteNumber, const Rect &screenRect, int scale, bool clipToScene = false);
+ void draw(SpriteList &spriteList, uint spriteNumber, const Rect &screenRect, int scale, bool clipToScene = false);
void loadList(int resourceId, SpriteList &spriteList); // load or append spriteList
- bool hitTest(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, const Point &testPoint);
- void getScaledSpriteBuffer(SpriteList &spriteList, int spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer);
+ bool hitTest(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, const Point &testPoint);
+ void getScaledSpriteBuffer(SpriteList &spriteList, uint spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer);
private:
void decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t outLength);
- void scaleBuffer(const byte *src, int width, int height, int scale);
+ void scaleBuffer(const byte *src, int width, int height, int scale, size_t outLength);
SagaEngine *_vm;
ResourceContext *_spriteContext;
- byte *_decodeBuf;
- size_t _decodeBufLen;
+ ByteArray _decodeBuf;
};
} // End of namespace Saga
diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp
index be674e5acd..0b59f3ba17 100644
--- a/engines/saga/sthread.cpp
+++ b/engines/saga/sthread.cpp
@@ -39,16 +39,18 @@ namespace Saga {
ScriptThread &Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) {
loadModule(scriptModuleNumber);
- if (_modules[scriptModuleNumber].entryPointsCount <= scriptEntryPointNumber) {
+ if (_modules[scriptModuleNumber].entryPoints.size() <= scriptEntryPointNumber) {
error("Script::createThread wrong scriptEntryPointNumber");
}
- ScriptThread newThread;
+ ScriptThread tmp;
+ _threadList.push_front(tmp);
+ ScriptThread &newThread = _threadList.front();
newThread._instructionOffset = _modules[scriptModuleNumber].entryPoints[scriptEntryPointNumber].offset;
- newThread._commonBase = _commonBuffer;
- newThread._staticBase = _commonBuffer + _modules[scriptModuleNumber].staticOffset;
- newThread._moduleBase = _modules[scriptModuleNumber].moduleBase;
- newThread._moduleBaseSize = _modules[scriptModuleNumber].moduleBaseSize;
+ newThread._commonBase = _commonBuffer.getBuffer();
+ newThread._staticBase = _commonBuffer.getBuffer() + _modules[scriptModuleNumber].staticOffset;
+ newThread._moduleBase = _modules[scriptModuleNumber].moduleBase.getBuffer();
+ newThread._moduleBaseSize = _modules[scriptModuleNumber].moduleBase.size();
newThread._strings = &_modules[scriptModuleNumber].strings;
if (_vm->getGameId() == GID_IHNM)
@@ -56,14 +58,10 @@ ScriptThread &Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntry
else
newThread._voiceLUT = &_modules[scriptModuleNumber].voiceLUT;
- _threadList.push_front(newThread);
-
+ newThread._stackBuf.resize(ScriptThread::THREAD_STACK_SIZE);
+ newThread._stackTopIndex = ScriptThread::THREAD_STACK_SIZE - 2;
debug(3, "createThread(). Total threads: %d", _threadList.size());
-
- ScriptThread &tmp = *_threadList.begin();
- tmp._stackBuf = (int16 *)malloc(ScriptThread::THREAD_STACK_SIZE * sizeof(int16));
- tmp._stackTopIndex = ScriptThread::THREAD_STACK_SIZE - 2;
- return tmp;
+ return newThread;
}
void Script::wakeUpActorThread(int waitType, void *threadObj) {
@@ -200,7 +198,7 @@ bool Script::runThread(ScriptThread &thread) {
bool breakOut = false;
int operandChar;
- MemoryReadStream scriptS(thread._moduleBase, thread._moduleBaseSize);
+ Common::MemoryReadStream scriptS(thread._moduleBase, thread._moduleBaseSize);
scriptS.seek(thread._instructionOffset);
@@ -211,7 +209,7 @@ bool Script::runThread(ScriptThread &thread) {
savedInstructionOffset = thread._instructionOffset;
operandChar = scriptS.readByte();
- debug(8, "Executing thread offset: %u (%x) stack: %d", thread._instructionOffset, operandChar, thread.pushedSize());
+ debug(8, "Executing thread offset: %u (0x%X) stack: %d", thread._instructionOffset, operandChar, thread.pushedSize());
stopParsing = false;
debug(4, "Calling op %s", this->_scriptOpsList[operandChar].scriptOpName);
diff --git a/engines/savestate.cpp b/engines/savestate.cpp
index 3cd81a2ff6..368f59ef51 100644
--- a/engines/savestate.cpp
+++ b/engines/savestate.cpp
@@ -70,3 +70,9 @@ void SaveStateDescriptor::setPlayTime(int hours, int minutes) {
snprintf(buffer, 32, "%.2d:%.2d", hours, minutes);
setVal("play_time", buffer);
}
+
+void SaveStateDescriptor::setPlayTime(uint32 msecs) {
+ uint minutes = msecs / 60000;
+ setPlayTime(minutes / 60, minutes % 60);
+}
+
diff --git a/engines/savestate.h b/engines/savestate.h
index ddbcea1acf..37f2b9bdd4 100644
--- a/engines/savestate.h
+++ b/engines/savestate.h
@@ -127,6 +127,11 @@ public:
* Sets the 'play_time' key properly, based on the given values.
*/
void setPlayTime(int hours, int minutes);
+
+ /**
+ * Sets the 'play_time' key properly, based on the given value.
+ */
+ void setPlayTime(uint32 msecs);
};
/** List of savestates. */
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 79f63fded4..4d458a42c8 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -39,6 +39,7 @@
#include "sci/sound/midiparser_sci.h"
#include "sci/sound/music.h"
#include "sci/sound/drivers/mididriver.h"
+#include "sci/sound/drivers/map-mt32-to-gm.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint.h"
@@ -57,6 +58,8 @@
#include "common/file.h"
#include "common/savefile.h"
+#include "engines/util.h"
+
namespace Sci {
int g_debug_sleeptime_factor = 1;
@@ -67,7 +70,7 @@ bool g_debug_track_mouse_clicks = false;
static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeValue);
Console::Console(SciEngine *engine) : GUI::Debugger(),
- _engine(engine), _debugState(engine->_debugState), _enterTime(0) {
+ _engine(engine), _debugState(engine->_debugState) {
// Variables
DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0);
@@ -103,7 +106,6 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("list", WRAP_METHOD(Console, cmdList));
DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep));
DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts));
- DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments));
// Game
DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame));
DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame));
@@ -145,6 +147,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("stopallsounds", WRAP_METHOD(Console, cmdStopAllSounds));
DCmd_Register("sfx01_header", WRAP_METHOD(Console, cmdSfx01Header));
DCmd_Register("sfx01_track", WRAP_METHOD(Console, cmdSfx01Track));
+ DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments));
+ DCmd_Register("map_instrument", WRAP_METHOD(Console, cmdMapInstrument));
// Script
DCmd_Register("addresses", WRAP_METHOD(Console, cmdAddresses));
DCmd_Register("registers", WRAP_METHOD(Console, cmdRegisters));
@@ -166,6 +170,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("snk", WRAP_METHOD(Console, cmdStepCallk)); // alias
DCmd_Register("disasm", WRAP_METHOD(Console, cmdDisassemble));
DCmd_Register("disasm_addr", WRAP_METHOD(Console, cmdDisassembleAddress));
+ DCmd_Register("find_callk", WRAP_METHOD(Console, cmdFindKernelFunctionCall));
DCmd_Register("send", WRAP_METHOD(Console, cmdSend));
DCmd_Register("go", WRAP_METHOD(Console, cmdGo));
DCmd_Register("logkernel", WRAP_METHOD(Console, cmdLogKernel));
@@ -218,20 +223,19 @@ Console::~Console() {
}
void Console::preEnter() {
- if (g_sci && g_sci->_soundCmd)
- g_sci->_soundCmd->pauseAll(true);
- _enterTime = g_system->getMillis();
+ _engine->pauseEngine(true);
}
-void Console::postEnter() {
- if (g_sci && g_sci->_soundCmd)
- g_sci->_soundCmd->pauseAll(false);
+extern void playVideo(Graphics::VideoDecoder *videoDecoder);
+void Console::postEnter() {
if (!_videoFile.empty()) {
- _engine->_gfxCursor->kernelHide();
-
Graphics::VideoDecoder *videoDecoder = 0;
+#ifdef ENABLE_SCI32
+ bool duckMode = false;
+#endif
+
if (_videoFile.hasSuffix(".seq")) {
SeqDecoder *seqDecoder = new SeqDecoder();
seqDecoder->setFrameDelay(_videoFrameDelay);
@@ -240,51 +244,51 @@ void Console::postEnter() {
} else if (_videoFile.hasSuffix(".vmd")) {
videoDecoder = new Graphics::VMDDecoder(g_system->getMixer());
#endif
+ } else if (_videoFile.hasSuffix(".duk")) {
+#ifdef ENABLE_SCI32
+ duckMode = true;
+ videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
+#else
+ warning("Duck videos require SCI32 support compiled in");
+#endif
} else if (_videoFile.hasSuffix(".avi")) {
videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
}
if (videoDecoder && videoDecoder->loadFile(_videoFile)) {
- uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
- uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
- bool skipVideo = false;
+ _engine->_gfxCursor->kernelHide();
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
-
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
- if (videoDecoder->needsUpdate()) {
- Graphics::Surface *frame = videoDecoder->decodeNextFrame();
- if (frame) {
- g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+#ifdef ENABLE_SCI32
+ // Duck videos are 16bpp, so we need to change pixel formats
+ int oldWidth = g_system->getWidth();
+ int oldHeight = g_system->getHeight();
+ if (duckMode) {
+ Common::List<Graphics::PixelFormat> formats;
+ formats.push_back(videoDecoder->getPixelFormat());
+ initGraphics(640, 480, true, formats);
+
+ if (g_system->getScreenFormat().bytesPerPixel != videoDecoder->getPixelFormat().bytesPerPixel)
+ error("Could not switch screen format for the duck video");
+ }
+#endif
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ playVideo(videoDecoder);
- g_system->updateScreen();
- }
- }
-
- 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;
- }
+#ifdef ENABLE_SCI32
+ // Switch back to 8bpp if we played a duck video
+ if (duckMode)
+ initGraphics(oldWidth, oldHeight, oldWidth > 320);
+#endif
- g_system->delayMillis(10);
- }
-
- delete videoDecoder;
+ _engine->_gfxCursor->kernelShow();
} else
warning("Could not play video %s\n", _videoFile.c_str());
- _engine->_gfxCursor->kernelShow();
_videoFile.clear();
_videoFrameDelay = 0;
}
- // Subtract the time we were running the debugger from the game running time
- _engine->_gamestate->gameStartTime += g_system->getMillis() - _enterTime;
+ _engine->pauseEngine(false);
}
bool Console::cmdHelp(int argc, const char **argv) {
@@ -332,7 +336,6 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" list - Lists all the resources of a given type\n");
DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n");
DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n");
- DebugPrintf(" show_instruments - Shows the instruments of a specific song, or all songs\n");
DebugPrintf("\n");
DebugPrintf("Game:\n");
DebugPrintf(" save_game - Saves the current game state to the hard disk\n");
@@ -372,6 +375,8 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" is_sample - Shows information on a given sound resource, if it's a PCM sample\n");
DebugPrintf(" sfx01_header - Dumps the header of a SCI01 song\n");
DebugPrintf(" sfx01_track - Dumps a track of a SCI01 song\n");
+ DebugPrintf(" show_instruments - Shows the instruments of a specific song, or all songs\n");
+ DebugPrintf(" map_instrument - Dynamically maps an MT-32 instrument to a GM instrument\n");
DebugPrintf("\n");
DebugPrintf("Script:\n");
DebugPrintf(" addresses - Provides information on how to pass addresses\n");
@@ -447,6 +452,10 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
DebugPrintf("Lofs type: %s\n", getSciVersionDesc(_engine->_features->detectLofsType()));
DebugPrintf("Move count type: %s\n", (_engine->_features->handleMoveCount()) ? "increment" : "ignore");
DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(_engine->_features->detectSetCursorType()));
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2)
+ DebugPrintf("kString type: %s\n", (_engine->_features->detectSci2StringFunctionType() == kSci2StringFunctionOld) ? "SCI2 (old)" : "SCI2.1 (new)");
+#endif
DebugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]);
DebugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette->isMerging() ? "yes" : "no");
DebugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc());
@@ -763,7 +772,7 @@ bool Console::cmdHexgrep(int argc, const char **argv) {
if (argc < 4) {
DebugPrintf("Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers.\n");
DebugPrintf("Usage: %s <resource type> <resource number> <search string>\n", argv[0]);
- DebugPrintf("<resource number> can be a specific resource number, or \"all\" for all of the resources of the specified type\n", argv[0]);
+ DebugPrintf("<resource number> can be a specific resource number, or \"all\" for all of the resources of the specified type\n");
DebugPrintf("EXAMPLES:\n hexgrep script all e8 03 c8 00\n hexgrep pic 042 fe");
cmdResourceTypes(argc, argv);
return true;
@@ -830,7 +839,7 @@ bool Console::cmdHexgrep(int argc, const char **argv) {
bool Console::cmdVerifyScripts(int argc, const char **argv) {
if (getSciVersion() < SCI_VERSION_1_1) {
- DebugPrintf("This script check is only meant for SCI1.1-SCI2.1 games\n");
+ DebugPrintf("This script check is only meant for SCI1.1-SCI3 games\n");
return true;
}
@@ -838,7 +847,7 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
Common::sort(resources->begin(), resources->end());
Common::List<ResourceId>::iterator itr = resources->begin();
- DebugPrintf("%d SCI1.1-SCI2.1 scripts found, performing sanity checks...\n", resources->size());
+ DebugPrintf("%d SCI1.1-SCI3 scripts found, performing sanity checks...\n", resources->size());
Resource *script, *heap;
while (itr != resources->end()) {
@@ -846,13 +855,19 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
if (!script)
DebugPrintf("Error: script %d couldn't be loaded\n", itr->getNumber());
- heap = _engine->getResMan()->findResource(ResourceId(kResourceTypeHeap, itr->getNumber()), false);
- if (!heap)
- DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->getNumber());
-
- if (script && heap && (script->size + heap->size > 65535))
- DebugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n",
- itr->getNumber(), script->size + heap->size);
+ if (getSciVersion() <= SCI_VERSION_2_1) {
+ heap = _engine->getResMan()->findResource(ResourceId(kResourceTypeHeap, itr->getNumber()), false);
+ if (!heap)
+ DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->getNumber());
+
+ if (script && heap && (script->size + heap->size > 65535))
+ DebugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n",
+ itr->getNumber(), script->size + heap->size);
+ } else { // SCI3
+ if (script && script->size > 65535)
+ DebugPrintf("Error: script %d is larger than 64KB (%d bytes)\n",
+ itr->getNumber(), script->size);
+ }
++itr;
}
@@ -863,6 +878,14 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
return true;
}
+// Same as in sound/drivers/midi.cpp
+uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins) {
+ if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM)
+ return Mt32Ins.gmRhythmKey + 0x80;
+ else
+ return Mt32Ins.gmInstr;
+}
+
bool Console::cmdShowInstruments(int argc, const char **argv) {
int songNumber = -1;
@@ -951,15 +974,14 @@ bool Console::cmdShowInstruments(int argc, const char **argv) {
DebugPrintf(" %d", instrument);
instruments[instrument]++;
instrumentsSongs[instrument][itr->getNumber()] = true;
+ } else {
+ channelData++;
}
break;
case 0xD:
channelData++; // param1
break;
case 0xB:
- channelData++; // param1
- channelData++; // param2
- break;
case 0x8:
case 0x9:
case 0xA:
@@ -1005,7 +1027,16 @@ bool Console::cmdShowInstruments(int argc, const char **argv) {
DebugPrintf("%d, ", i);
}
DebugPrintf("\n\n");
+ }
+
+ DebugPrintf("Instruments not mapped in the MT32->GM map: ");
+ for (int i = 0; i < 128; i++) {
+ if (instruments[i] > 0 && getGmInstrument(Mt32MemoryTimbreMaps[i]) == MIDI_UNMAPPED)
+ DebugPrintf("%d, ", i);
+ }
+ DebugPrintf("\n\n");
+ if (songNumber == -1) {
DebugPrintf("Used instruments in songs:\n");
for (int i = 0; i < 128; i++) {
if (instruments[i] > 0) {
@@ -1025,6 +1056,43 @@ bool Console::cmdShowInstruments(int argc, const char **argv) {
return true;
}
+bool Console::cmdMapInstrument(int argc, const char **argv) {
+ if (argc != 4) {
+ DebugPrintf("Maps an MT-32 custom instrument to a GM instrument on the fly\n\n");
+ DebugPrintf("Usage %s <MT-32 instrument name> <GM instrument> <GM rhythm key>\n", argv[0]);
+ DebugPrintf("Each MT-32 instrument is always 10 characters and is mapped to either a GM instrument, or a GM rhythm key\n");
+ DebugPrintf("A value of 255 (0xff) signifies an unmapped instrument\n");
+ DebugPrintf("Please replace the spaces in the instrument name with underscores (\"_\"). They'll be converted to spaces afterwards\n\n");
+ DebugPrintf("Example: %s test_0__XX 1 255\n", argv[0]);
+ DebugPrintf("The above example will map the MT-32 instrument \"test 0 XX\" to GM instrument 1\n\n");
+ } else {
+ if (Mt32dynamicMappings != NULL) {
+ Mt32ToGmMap newMapping;
+ char *instrumentName = new char[11];
+ Common::strlcpy(instrumentName, argv[1], 11);
+
+ for (uint16 i = 0; i < strlen(instrumentName); i++)
+ if (instrumentName[i] == '_')
+ instrumentName[i] = ' ';
+
+ newMapping.name = instrumentName;
+ newMapping.gmInstr = atoi(argv[2]);
+ newMapping.gmRhythmKey = atoi(argv[3]);
+ Mt32dynamicMappings->push_back(newMapping);
+ }
+ }
+
+ DebugPrintf("Current dynamic mappings:\n");
+ if (Mt32dynamicMappings != NULL) {
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ DebugPrintf("\"%s\" -> %d / %d\n", (*it).name, (*it).gmInstr, (*it).gmRhythmKey);
+ }
+ }
+
+ return true;
+}
+
bool Console::cmdList(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Lists all the resources of a given type\n");
@@ -1041,7 +1109,7 @@ bool Console::cmdList(int argc, const char **argv) {
if ((res == kResourceTypeAudio36) || (res == kResourceTypeSync36)) {
if (argc != 3) {
- DebugPrintf("Please specify map number\n");
+ DebugPrintf("Please specify map number (-1: all maps)\n");
return true;
}
number = atoi(argv[2]);
@@ -1140,14 +1208,18 @@ bool Console::cmdRestartGame(int argc, const char **argv) {
}
bool Console::cmdClassTable(int argc, const char **argv) {
- DebugPrintf("Available classes:\n");
+ DebugPrintf("Available classes (parse a parameter to filter the table by a specific class):\n");
+
for (uint i = 0; i < _engine->_gamestate->_segMan->classTableSize(); i++) {
Class temp = _engine->_gamestate->_segMan->_classTable[i];
if (temp.reg.segment) {
- DebugPrintf(" Class 0x%x (%s) at %04x:%04x (script 0x%x)\n", i,
- _engine->_gamestate->_segMan->getObjectName(temp.reg),
- PRINT_REG(temp.reg),
- temp.script);
+ const char *className = _engine->_gamestate->_segMan->getObjectName(temp.reg);
+ if (argc == 1 || (argc == 2 && !strcmp(className, argv[1]))) {
+ DebugPrintf(" Class 0x%x (%s) at %04x:%04x (script %d)\n", i,
+ className,
+ PRINT_REG(temp.reg),
+ temp.script);
+ } else DebugPrintf(" Class 0x%x (not loaded; can't get name) (script %d)\n", i, temp.script);
}
}
@@ -1327,9 +1399,9 @@ bool Console::cmdSaid(int argc, const char **argv) {
}
spec[len++] = 0xFF;
- printf("Matching '%s' against:", string);
+ debugN("Matching '%s' against:", string);
_engine->getVocabulary()->debugDecipherSaidBlock(spec);
- printf("\n");
+ debugN("\n");
ResultWordListList words;
bool res = _engine->getVocabulary()->tokenizeString(words, string, &error);
@@ -1491,7 +1563,7 @@ bool Console::cmdPicVisualize(int argc, const char **argv) {
bool Console::cmdPlayVideo(int argc, const char **argv) {
if (argc < 2) {
- DebugPrintf("Plays a SEQ, AVI or VMD video.\n");
+ DebugPrintf("Plays a SEQ, AVI, DUK or VMD video.\n");
DebugPrintf("Usage: %s <video file name> <delay>\n", argv[0]);
DebugPrintf("The video file name should include the extension\n");
DebugPrintf("Delay is only used in SEQ videos and is measured in ticks (default: 10)\n");
@@ -1501,7 +1573,7 @@ bool Console::cmdPlayVideo(int argc, const char **argv) {
Common::String filename = argv[1];
filename.toLowercase();
- if (filename.hasSuffix(".seq") || filename.hasSuffix(".avi") || filename.hasSuffix(".vmd")) {
+ if (filename.hasSuffix(".seq") || filename.hasSuffix(".avi") || filename.hasSuffix(".vmd") || filename.hasSuffix(".duk")) {
_videoFile = filename;
_videoFrameDelay = (argc == 2) ? 10 : atoi(argv[2]);
return Cmd_Exit(0, 0);
@@ -2546,13 +2618,18 @@ bool Console::cmdStepCallk(int argc, const char **argv) {
}
bool Console::cmdDisassemble(int argc, const char **argv) {
- if (argc != 3) {
+ if (argc < 3) {
DebugPrintf("Disassembles a method by name.\n");
- DebugPrintf("Usage: %s <object> <method>\n", argv[0]);
+ DebugPrintf("Usage: %s <object> <method> <options>\n", argv[0]);
+ DebugPrintf("Valid options are:\n");
+ DebugPrintf(" bwt : Print byte/word tag\n");
+ DebugPrintf(" bc : Print bytecode\n");
return true;
}
reg_t objAddr = NULL_REG;
+ bool printBytecode = false;
+ bool printBWTag = false;
if (parse_reg_t(_engine->_gamestate, argv[1], &objAddr, false)) {
DebugPrintf("Invalid address passed.\n");
@@ -2579,8 +2656,15 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
return true;
}
+ for (int i = 3; i < argc; i++) {
+ if (!scumm_stricmp(argv[i], "bwt"))
+ printBytecode = true;
+ else if (!scumm_stricmp(argv[i], "bc"))
+ printBWTag = true;
+ }
+
do {
- addr = disassemble(_engine->_gamestate, addr, 0, 0);
+ addr = disassemble(_engine->_gamestate, addr, printBWTag, printBytecode);
} while (addr.offset > 0);
return true;
@@ -2598,9 +2682,9 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
}
reg_t vpc = NULL_REG;
- int op_count = 1;
- int do_bwc = 0;
- int do_bytes = 0;
+ int opCount = 1;
+ bool printBWTag = false;
+ bool printBytes = false;
int size;
if (parse_reg_t(_engine->_gamestate, argv[1], &vpc, false)) {
@@ -2614,25 +2698,109 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
for (int i = 2; i < argc; i++) {
if (!scumm_stricmp(argv[i], "bwt"))
- do_bwc = 1;
+ printBWTag = true;
else if (!scumm_stricmp(argv[i], "bc"))
- do_bytes = 1;
+ printBytes = true;
else if (toupper(argv[i][0]) == 'C')
- op_count = atoi(argv[i] + 1);
+ opCount = atoi(argv[i] + 1);
else {
DebugPrintf("Invalid option '%s'\n", argv[i]);
return true;
}
}
- if (op_count < 0) {
+ if (opCount < 0) {
DebugPrintf("Invalid op_count\n");
return true;
}
do {
- vpc = disassemble(_engine->_gamestate, vpc, do_bwc, do_bytes);
- } while ((vpc.offset > 0) && (vpc.offset + 6 < size) && (--op_count));
+ vpc = disassemble(_engine->_gamestate, vpc, printBWTag, printBytes);
+ } while ((vpc.offset > 0) && (vpc.offset + 6 < size) && (--opCount));
+
+ return true;
+}
+
+bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Finds the scripts and methods that call a specific kernel function.\n");
+ DebugPrintf("Usage: %s <kernel function>\n", argv[0]);
+ DebugPrintf("Example: %s Display\n", argv[0]);
+ return true;
+ }
+
+ // Find the number of the kernel function call
+ int kernelFuncNum = _engine->getKernel()->findKernelFuncPos(argv[1]);
+
+ if (kernelFuncNum < 0) {
+ DebugPrintf("Invalid kernel function requested\n");
+ return true;
+ }
+
+ Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript);
+ Common::sort(resources->begin(), resources->end());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+
+ DebugPrintf("%d scripts found, dissassembling...\n", resources->size());
+
+ int scriptSegment;
+ Script *script;
+ SegManager *segMan = _engine->getEngineState()->_segMan;
+
+ while (itr != resources->end()) {
+ if (_engine->getGameId() == GID_KQ5 && itr->getNumber() == 980) {
+ // Ignore script 980 in KQ5. Seems to be a leftover, as it
+ // uses a superclass from script 988, which doesn't exist
+ itr++;
+ continue;
+ }
+
+ // Load script
+ scriptSegment = segMan->instantiateScript(itr->getNumber());
+ script = segMan->getScript(scriptSegment);
+
+ // Iterate through all the script's objects
+ ObjMap::iterator it;
+ const ObjMap::iterator end = script->_objects.end();
+ for (it = script->_objects.begin(); it != end; ++it) {
+ const Object *obj = segMan->getObject(it->_value.getPos());
+ const char *objName = segMan->getObjectName(it->_value.getPos());
+
+ // Now dissassemble each method of the script object
+ for (uint16 i = 0; i < obj->getMethodCount(); i++) {
+ reg_t fptr = obj->getFunction(i);
+ uint16 offset = fptr.offset;
+ int16 opparams[4];
+ byte extOpcode;
+ byte opcode;
+
+ while (true) {
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
+ opcode = extOpcode >> 1;
+
+ if (opcode == op_callk) {
+ uint16 kFuncNum = opparams[0];
+ uint16 argc2 = opparams[1];
+
+ if (kFuncNum == kernelFuncNum) {
+ DebugPrintf("Called from script %d, object %s, method %s(%d) with %d parameters\n",
+ itr->getNumber(), objName,
+ _engine->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), i, argc2);
+ }
+ }
+
+ // Check for end of function/script
+ if (opcode == op_ret || offset >= script->getBufSize())
+ break;
+ } // while (true)
+ } // for (uint16 i = 0; i < obj->getMethodCount(); i++)
+ } // for (it = script->_objects.begin(); it != end; ++it)
+
+ segMan->uninstantiateScript(itr->getNumber());
+ ++itr;
+ }
+
+ delete resources;
return true;
}
@@ -2641,7 +2809,7 @@ bool Console::cmdSend(int argc, const char **argv) {
if (argc < 3) {
DebugPrintf("Sends a message to an object.\n");
DebugPrintf("Usage: %s <object> <selector name> <param1> <param2> ... <paramn>\n", argv[0]);
- DebugPrintf("Example: send ?fooScript cue\n");
+ DebugPrintf("Example: %s ?fooScript cue\n", argv[0]);
return true;
}
@@ -3021,24 +3189,24 @@ static void midi_hexdump(byte *data, int size, int notational_offset) {
int blanks = 0;
offset += offset_mod;
- printf(" [%04x] %d\t",
+ debugN(" [%04x] %d\t",
old_offset + notational_offset, time);
cmd = data[offset];
if (!(cmd & 0x80)) {
cmd = prev;
if (prev < 0x80) {
- printf("Track broken at %x after"
+ debugN("Track broken at %x after"
" offset mod of %d\n",
offset + notational_offset, offset_mod);
Common::hexdump(data, size, 16, notational_offset);
return;
}
- printf("(rs %02x) ", cmd);
+ debugN("(rs %02x) ", cmd);
blanks += 8;
} else {
++offset;
- printf("%02x ", cmd);
+ debugN("%02x ", cmd);
blanks += 3;
}
prev = cmd;
@@ -3050,37 +3218,37 @@ static void midi_hexdump(byte *data, int size, int notational_offset) {
for (i = 0; i < pleft; i++) {
if (i == 0)
firstarg = data[offset];
- printf("%02x ", data[offset++]);
+ debugN("%02x ", data[offset++]);
blanks += 3;
}
while (blanks < 16) {
blanks += 4;
- printf(" ");
+ debugN(" ");
}
while (blanks < 20) {
++blanks;
- printf(" ");
+ debugN(" ");
}
if (cmd == SCI_MIDI_EOT)
- printf(";; EOT");
+ debugN(";; EOT");
else if (cmd == SCI_MIDI_SET_SIGNAL) {
if (firstarg == SCI_MIDI_SET_SIGNAL_LOOP)
- printf(";; LOOP point");
+ debugN(";; LOOP point");
else
- printf(";; CUE (%d)", firstarg);
+ debugN(";; CUE (%d)", firstarg);
} else if (SCI_MIDI_CONTROLLER(cmd)) {
if (firstarg == SCI_MIDI_CUMULATIVE_CUE)
- printf(";; CUE (cumulative)");
+ debugN(";; CUE (cumulative)");
else if (firstarg == SCI_MIDI_RESET_ON_SUSPEND)
- printf(";; RESET-ON-SUSPEND flag");
+ debugN(";; RESET-ON-SUSPEND flag");
}
- printf("\n");
+ debugN("\n");
if (old_offset >= offset) {
- printf("-- Not moving forward anymore,"
+ debugN("-- Not moving forward anymore,"
" aborting (%x/%x)\n", offset, old_offset);
return;
}
@@ -3518,7 +3686,7 @@ int Console::printObject(reg_t pos) {
DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), s->_segMan->getObjectName(pos),
obj->getVarCount(), obj->getMethodCount());
- if (!obj->isClass())
+ if (!obj->isClass() && getSciVersion() != SCI_VERSION_3)
var_container = s->_segMan->getObject(obj->getSuperClassSelector());
DebugPrintf(" -- member variables:\n");
for (i = 0; (uint)i < obj->getVarCount(); i++) {
@@ -3559,22 +3727,22 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO
byte c;
int offset = startOffset;
while (len >= regsPerLine) {
- printf("%06x: ", offset);
+ debugN("%06x: ", offset);
for (i = 0; i < regsPerLine; i++) {
- printf("%04x:%04x ", PRINT_REG(data[i]));
+ debugN("%04x:%04x ", PRINT_REG(data[i]));
}
- printf(" |");
+ debugN(" |");
for (i = 0; i < regsPerLine; i++) {
c = data[i].toUint16() & 0xff;
if (c < 32 || c >= 127)
c = '.';
- printf("%c", c);
+ debugN("%c", c);
c = data[i].toUint16() >> 8;
if (c < 32 || c >= 127)
c = '.';
- printf("%c", c);
+ debugN("%c", c);
}
- printf("|\n");
+ debugN("|\n");
data += regsPerLine;
len -= regsPerLine;
offset += regsPerLine * (isArray ? 1 : 2);
@@ -3583,27 +3751,27 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO
if (len <= 0)
return;
- printf("%06x: ", offset);
+ debugN("%06x: ", offset);
for (i = 0; i < regsPerLine; i++) {
if (i < len)
- printf("%04x:%04x ", PRINT_REG(data[i]));
+ debugN("%04x:%04x ", PRINT_REG(data[i]));
else
- printf(" ");
+ debugN(" ");
}
- printf(" |");
+ debugN(" |");
for (i = 0; i < len; i++) {
c = data[i].toUint16() & 0xff;
if (c < 32 || c >= 127)
c = '.';
- printf("%c", c);
+ debugN("%c", c);
c = data[i].toUint16() >> 8;
if (c < 32 || c >= 127)
c = '.';
- printf("%c", c);
+ debugN("%c", c);
}
for (; i < regsPerLine; i++)
- printf(" ");
- printf("|\n");
+ debugN(" ");
+ debugN("|\n");
}
} // End of namespace Sci
diff --git a/engines/sci/console.h b/engines/sci/console.h
index 8bc2da439c..d9f1408b41 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -36,7 +36,7 @@ namespace Sci {
class SciEngine;
struct List;
-reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode);
+reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode);
class Console : public GUI::Debugger {
public:
@@ -75,7 +75,6 @@ private:
bool cmdList(int argc, const char **argv);
bool cmdHexgrep(int argc, const char **argv);
bool cmdVerifyScripts(int argc, const char **argv);
- bool cmdShowInstruments(int argc, const char **argv);
// Game
bool cmdSaveGame(int argc, const char **argv);
bool cmdRestoreGame(int argc, const char **argv);
@@ -115,6 +114,8 @@ private:
bool cmdStopAllSounds(int argc, const char **argv);
bool cmdSfx01Header(int argc, const char **argv);
bool cmdSfx01Track(int argc, const char **argv);
+ bool cmdShowInstruments(int argc, const char **argv);
+ bool cmdMapInstrument(int argc, const char **argv);
// Script
bool cmdAddresses(int argc, const char **argv);
bool cmdRegisters(int argc, const char **argv);
@@ -128,6 +129,7 @@ private:
bool cmdStepCallk(int argc, const char **argv);
bool cmdDisassemble(int argc, const char **argv);
bool cmdDisassembleAddress(int argc, const char **argv);
+ bool cmdFindKernelFunctionCall(int argc, const char **argv);
bool cmdSend(int argc, const char **argv);
bool cmdGo(int argc, const char **argv);
bool cmdLogKernel(int argc, const char **argv);
@@ -166,7 +168,6 @@ private:
bool _mouseVisible;
Common::String _videoFile;
int _videoFrameDelay;
- uint32 _enterTime;
};
} // End of namespace Sci
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index a4d1edf2ed..a4914e57b4 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -29,6 +29,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
#include "sci/sci.h"
#include "sci/engine/kernel.h"
@@ -118,7 +119,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
{"lsl7", "Leisure Suit Larry 7: Love for Sail!"},
{"lighthouse", "Lighthouse: The Dark Being"},
{"phantasmagoria2", "Phantasmagoria II: A Puzzle of Flesh"},
- {"shivers2", "Shivers II: Harvest of Souls"},
+ //{"shivers2", "Shivers II: Harvest of Souls"}, // Not SCI
{"rama", "RAMA"},
{0, 0}
};
@@ -190,7 +191,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = {
{ "rama", GID_RAMA },
{ "sci-fanmade", GID_FANMADE }, // FIXME: Do we really need/want this?
{ "shivers", GID_SHIVERS },
- { "shivers2", GID_SHIVERS2 },
+ //{ "shivers2", GID_SHIVERS2 }, // Not SCI
{ "slater", GID_SLATER },
{ "sq1sci", GID_SQ1 },
{ "sq3", GID_SQ3 },
@@ -513,7 +514,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
ResourceManager *resMan = new ResourceManager();
assert(resMan);
resMan->addAppropriateSources(fslist);
- resMan->init();
+ resMan->init(true);
// TODO: Add error handling.
#ifndef ENABLE_SCI32
@@ -624,7 +625,8 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const {
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
- (f == kSavesSupportCreationDate);
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime);
}
bool SciEngine::hasFeature(EngineFeature f) const {
@@ -665,7 +667,7 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
delete in;
continue;
}
- saveList.push_back(SaveStateDescriptor(slotNum, meta.savegame_name));
+ saveList.push_back(SaveStateDescriptor(slotNum, meta.name));
delete in;
}
}
@@ -675,7 +677,7 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
}
SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
- Common::String fileName = Common::String::printf("%s.%03d", target, slot);
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
if (in) {
@@ -688,7 +690,7 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
return desc;
}
- SaveStateDescriptor desc(slot, meta.savegame_name);
+ SaveStateDescriptor desc(slot, meta.name);
Graphics::Surface *thumbnail = new Graphics::Surface();
assert(thumbnail);
@@ -702,18 +704,18 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
desc.setDeletableFlag(true);
desc.setWriteProtectedFlag(false);
- int day = (meta.savegame_date >> 24) & 0xFF;
- int month = (meta.savegame_date >> 16) & 0xFF;
- int year = meta.savegame_date & 0xFFFF;
+ int day = (meta.saveDate >> 24) & 0xFF;
+ int month = (meta.saveDate >> 16) & 0xFF;
+ int year = meta.saveDate & 0xFFFF;
desc.setSaveDate(year, month, day);
- int hour = (meta.savegame_time >> 16) & 0xFF;
- int minutes = (meta.savegame_time >> 8) & 0xFF;
+ int hour = (meta.saveTime >> 16) & 0xFF;
+ int minutes = (meta.saveTime >> 8) & 0xFF;
desc.setSaveTime(hour, minutes);
- // TODO: played time
+ desc.setPlayTime(meta.playTime * 1000);
delete in;
@@ -726,12 +728,12 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
int SciMetaEngine::getMaximumSaveSlot() const { return 99; }
void SciMetaEngine::removeSaveState(const char *target, int slot) const {
- Common::String fileName = Common::String::printf("%s.%03d", target, slot);
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
g_system->getSavefileManager()->removeSavefile(fileName);
}
Common::Error SciEngine::loadGameState(int slot) {
- Common::String fileName = Common::String::printf("%s.%03d", _targetName.c_str(), slot);
+ Common::String fileName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
Common::SeekableReadStream *in = saveFileMan->openForLoading(fileName);
@@ -750,7 +752,7 @@ Common::Error SciEngine::loadGameState(int slot) {
}
Common::Error SciEngine::saveGameState(int slot, const char *desc) {
- Common::String fileName = Common::String::printf("%s.%03d", _targetName.c_str(), slot);
+ Common::String fileName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
Common::OutSaveFile *out = saveFileMan->openForSaving(fileName);
const char *version = "";
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index e1d03d9914..8d89b7de1b 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -26,7 +26,7 @@
namespace Sci {
// SCI3 games have a different script format (in CSC files) and are currently unsupported
-//#define ENABLE_SCI3_GAMES
+#define ENABLE_SCI3_GAMES
#define FANMADE_L(name, resMapMd5, resMapSize, resMd5, resSize, lang) \
{"sci-fanmade", name, { \
@@ -39,6 +39,7 @@ namespace Sci {
using Common::GUIO_NONE;
using Common::GUIO_NOSPEECH;
+using Common::GUIO_MIDIGM;
// Game descriptions
static const struct ADGameDescription SciGameDescriptions[] = {
@@ -413,7 +414,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a4b73d5d2b55bdb6e44345e99c8fbdd0", 4804},
{"resource.000", 0, "d908dbef56816ac6c60dd145fdeafb2b", 3536046},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
+
+ // Eco Quest - English DOS CD 1.1
+ // SCI interpreter version 1.001.064
+ // Same entry as the DOS version above. This one is used for the alternate
+ // General MIDI music tracks in the Windows version
+ {"ecoquest", "CD", {
+ {"resource.map", 0, "a4b73d5d2b55bdb6e44345e99c8fbdd0", 4804},
+ {"resource.000", 0, "d908dbef56816ac6c60dd145fdeafb2b", 3536046},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO_MIDIGM },
// Eco Quest - English DOS Floppy
// SCI interpreter version 1.000.510
@@ -503,13 +514,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Freddy Pharkas - English CD (from FRG)
+ // Freddy Pharkas - English CD DOS (from FRG)
// SCI interpreter version 1.001.132
{"freddypharkas", "CD", {
{"resource.map", 0, "d46b282f228a67ba13bd4b4009e95f8f", 6058},
{"resource.000", 0, "ee3c64ffff0ba9fb08bea2624631c598", 5490246},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Freddy Pharkas - English DOS Floppy (updated information from markcoolio in bug reports #2723773 and #2724720)
// Executable scanning reports "1.cfs.081"
@@ -551,7 +562,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "05acdc256c742e79c50b9fe7ec2cc898", 863310},
{"resource.msg", 0, "45b5bf74933ac3727e4cc844446dc052", 796156},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Freddy Pharkas - Spanish DOS (from jvprat)
// Executable scanning reports "1.cfs.081", VERSION file reports "1.000, March 30, 1995"
@@ -618,7 +629,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
+
+ // Gabriel Knight - English Windows CD (from jvprat)
+ // Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
+ {"gk1", "CD", {
+ {"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
+ {"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO_NONE },
// Gabriel Knight - German DOS CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -626,7 +645,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Gabriel Knight - Spanish DOS CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -634,7 +653,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Gabriel Knight - French DOS CD (from Hkz)
// VERSION file reports "1.000.000, May 3, 1994"
@@ -642,15 +661,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "55f909ba93a2515042a08d8a2da8414e", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13325145},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE },
-
- // Gabriel Knight - English Windows CD (from jvprat)
- // Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
- {"gk1", "CD", {
- {"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
- {"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
- AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Gabriel Knight - German Windows CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -658,7 +669,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, 0, GUIO_NONE },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_CD, GUIO_NONE },
// Gabriel Knight - Spanish Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -666,7 +677,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformWindows, 0, GUIO_NONE },
+ Common::ES_ESP, Common::kPlatformWindows, ADGF_CD, GUIO_NONE },
// Gabriel Knight - English Macintosh
{"gk1", "", {
@@ -752,10 +763,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Hoyle 1 - English DOS (supplied by eddydrama in bug report #3052366)
+ // Hoyle 1 3.5' - English DOS (supplied by eddydrama in bug report #3052366 and dinnerx in bug report #3090841)
{"hoyle1", "", {
{"resource.map", 0, "0af9a3dcd72a091960de070432e1f524", 4386},
- {"resource.001", 0, "e0dd44069a62463fd124974b915f10d", 518127},
+ {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518127},
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
@@ -854,14 +865,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Hoyle 4 - English DOS Demo
+ // Hoyle 4 (Hoyle Classic Card Games) - English DOS Demo
{"hoyle4", "Demo", {
{"resource.map", 0, "60f764020a6b788bbbe415dbc2ccb9f3", 931},
{"resource.000", 0, "5fe3670e3ddcd4f85c10013b5453141a", 615522},
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Hoyle 4 - English DOS Demo
+ // Hoyle 4 (Hoyle Classic Card Games) - English DOS Demo
// SCI interpreter version 1.001.200 (just a guess)
// Does anyone have this version? -clone2727
{"hoyle4", "Demo", {
@@ -871,7 +882,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Hoyle 4 (Hoyle Classic Card Games) - English DOS/Win
- // SCI1.1
// Supplied by abevi in bug report #3039291
{"hoyle4", "", {
{"resource.map", 0, "2b577c975cc8d8d43f61b6a756129fe3", 4352},
@@ -879,6 +889,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Hoyle 4 (Hoyle Classic Card Games) - English Macintosh Floppy
+ // VERSION file reports "2.0"
+ {"hoyle4", "", {
+ {"Data1", 0, "afad082944d36ce4d2a9e646efc49da1", 7731536},
+ {"Data2", 0, "615ed2efe969f845cd8f0686af0b06f2", 1543825},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO_NOSPEECH },
+
// Jones in the Fast Lane EGA - English DOS
// SCI interpreter version 1.000.172 (not 100% sure FIXME)
{"jones", "EGA", {
@@ -919,6 +937,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
+ // Jones in the Fast Lane - English DOS CD
+ // Same entry as the DOS version above. This one is used for the alternate
+ // General MIDI music tracks in the Windows version
+ {"jones", "CD", {
+ {"resource.map", 0, "459f5b04467bc2107aec02f5c4b71b37", 4878},
+ {"resource.001", 0, "3876da2ce16fb7dea2f5d943d946fa84", 1652150},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO_MIDIGM },
+
// King's Quest 1 SCI Remake - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.003.007"
// SCI interpreter version 0.001.010
@@ -1050,6 +1077,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // King's Quest 4 - English Atari ST (double-sided diskettes)
+ // Game version 1.003.006 (January 12, 1989)
+ // SCI interpreter version 1.001.008
+ // Provided by fischersfritz in bug report #3110941
+ {"kq4sci", "", {
+ {"resource.map", 0, "8800cd62b1eee93752099986dc704a16", 7416},
+ {"resource.001", 0, "a3cdb4848fb859fdd302976fff56490f", 450790},
+ {"resource.002", 0, "a3cdb4848fb859fdd302976fff56490f", 535276},
+ {"resource.003", 0, "a3cdb4848fb859fdd302976fff56490f", 705074},
+ {"resource.004", 0, "a3cdb4848fb859fdd302976fff56490f", 478366},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO_NOSPEECH },
+
// King's Quest 5 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.004.018"
// SCI interpreter version 1.000.060
@@ -1106,7 +1146,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "449471bfd77be52f18a3773c7f7d843d", 571368},
{"resource.001", 0, "b45a581ff8751e052c7e364f58d3617f", 16800210},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
+
+ // King's Quest 5 - English DOS CD (from the King's Quest Collection)
+ // Executable scanning reports "x.yyy.zzz", VERSION file reports "1.000.052"
+ // SCI interpreter version 1.000.784
+ // Same entry as the DOS version above. This one is used for the alternate
+ // MIDI music tracks in the Windows version
+ {"kq5", "CD", {
+ {"resource.map", 0, "f68ba690e5920725dcf9328001b90e33", 13122},
+ {"resource.000", 0, "449471bfd77be52f18a3773c7f7d843d", 571368},
+ {"resource.001", 0, "b45a581ff8751e052c7e364f58d3617f", 16800210},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO_MIDIGM },
// King's Quest 5 - English DOS Floppy
// SCI interpreter version 1.000.060
@@ -1324,7 +1376,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215},
{"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// King's Quest 6 - English Windows CD (from the King's Quest Collection)
// Executable scanning reports "1.cfs.158", VERSION file reports "1.034 9/11/94 - KQ6 version 1.000.00G"
@@ -1333,7 +1385,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215},
{"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO_NONE },
// King's Quest 6 - Spanish DOS CD (from jvprat)
// Executable scanning reports "1.cfs.158", VERSION file reports "1.000.000, July 5, 1994"
@@ -1343,7 +1395,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "4da3ad5868a775549a7cc4f72770a58e", 8537260},
{"resource.msg", 0, "41eed2d3893e1ca6c3695deba4e9d2e8", 267102},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// King's Quest 6 - English Macintosh Floppy
// VERSION file reports "1.0"
@@ -1473,20 +1525,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Laura Bow - German DOS (from Tobis87, also includes english language)
- // SCI interpreter version 0.000.631 (or 0.000.685?)
- {"laurabow", "", {
- {"resource.map", 0, "b1905f6aa68ff65a057b080b1eae954c", 12030},
- {"resource.001", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 108032},
- {"resource.002", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 354680},
- {"resource.003", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 361815},
- {"resource.004", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 339714},
- {"resource.005", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 327465},
- {"resource.006", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 328390},
- {"resource.007", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 317687},
- AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
-
// Laura Bow 2 - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "x.yyy.zzz"
// SCI interpreter version 1.001.069 (just a guess)
@@ -1512,7 +1550,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a70945e61ba7ac7bfea6b7bd72c6aec5", 7274},
{"resource.000", 0, "82578b8d5a7e09c4c58891ca49fae35b", 5598672},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Laura Bow 2 v1.1 - French DOS Floppy (from Hkz)
{"laurabow2", "", {
@@ -1538,7 +1576,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "71f1f0cd9f082da2e750c793a8ed9d84", 286141},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Larry 1 EGA Remake - English DOS (from spookypeanut)
// SCI interpreter version 0.000.510 (or 0.000.577?)
@@ -2206,7 +2244,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "1c7f311b0a2c927b2fbe81ae341fb2f6", 5790},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 4369438},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
// Mixed-Up Mother Goose - English Windows Interactive Demo
// Executable scanning reports "x.yyy.zzz"
@@ -2278,6 +2316,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+ // Phantasmagoria - English DOS/Windows (GOG version) - ressci.* merged in ressci.000
+ // Windows executable scanning reports "2.100.002" - "Sep 19 1995 15:09:43"
+ // DOS executable scanning reports "2.100.002" - "Sep 19 1995 09:15:40"
+ // VERSION file reports "1.100.001UK"
+ // Supplied by littleboy in patch #3112884
+ {"phantasmagoria", "", {
+ {"ressci.000", 0, "cd5967f9b9586e3380645961c0765be3", 116822037},
+ {"resmap.000", 0, "3cafc1c6a53945c1f3babbfd6380c64c", 16468},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
#ifdef ENABLE_SCI3_GAMES
// Phantasmagoria 2 - English Windows (from jvprat)
// Executable scanning reports "3.000.000", VERSION file reports "001.0.06"
@@ -2294,6 +2343,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.005", 0, "05f9fe2bee749659acb3cd2c90252fc5", 67905112},
AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
+
+ // Phantasmagoria 2 - English DOS (GOG version) - ressci.* merged in ressci.000
+ // Executable scanning reports "3.000.000" - "Dec 07 1996 09:29:03"
+ // VERSION file reports "001.0.06"
+ // Supplied by littleboy in patch #3112884
+ {"phantasmagoria2", "", {
+ {"ressci.000", 0, "c54f26d9f43f908151263254b6d97053", 108134481},
+ {"resmap.000", 0, "de154a223a9ef4ea7358b76adc38ef5b", 2956},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
#endif // ENABLE_SCI3_GAMES
#endif // ENABLE_SCI32
@@ -2929,7 +2988,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NONE },
-#ifdef ENABLE_SCI3_GAMES
+ // Shivers 2 doesn't contain SCI scripts. The whole game logic has
+ // been reimplemented from SCI in native code placed in DLL files.
+ // Each room has its own DLL file, and some SCI functions have been
+ // reimplemented/rewritten for this purpose in native code. The
+ // game and demo have all the resources of a SCI game, apart from
+ // the SCI scripts themselves. Thus, they cannot be directly
+ // supported, unless their whole room logic is rewritten from
+ // scratch, which classifies Shivers 2 as "not SCI"
+#if 0
// Shivers2 - English Windows Demo
// Executable scanning reports "3.000.000"
@@ -2947,7 +3014,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, 0, GUIO_NOSPEECH },
-#endif //ENABLE_SCI3_GAMES
+#endif
#endif // ENABLE_SCI32
@@ -3276,7 +3343,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054},
{"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO_NONE },
+
+ // Space Quest 4 - English Windows CD (from the Space Quest Collection)
+ // Executable scanning reports "1.001.064", VERSION file reports "1.0"
+ // Same entry as the DOS version above. This one is used for the alternate
+ // General MIDI music tracks in the Windows version
+ {"sq4", "CD", {
+ {"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054},
+ {"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO_MIDIGM },
// Space Quest 4 - Spanish DOS CD (from jvprat, is still text only, not talkie, also includes english language)
// Executable scanning reports "1.SQ4.057", VERSION file reports "1.000"
@@ -3333,6 +3410,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
+ // Space Quest 4 - Russian DOS
+ // Executable scanning reports "1.000.753", VERSION file reports "1.994"
+ {"sq4", "", {
+ {"resource.map", 0, "e4f77dd99012d51e16903da07769a7bf", 5928},
+ {"resource.000", 0, "e1f46832cd2458796028e054a0466031", 186750},
+ {"resource.001", 0, "1110371c3bafbbf8968a324097c83fdb", 1283759},
+ {"resource.002", 0, "9c342cd76b421369406d6fafd7b1a285", 1234726},
+ {"resource.003", 0, "e617f09840d9f86181f7602c8bf2e8ad", 1266491},
+ {"resource.004", 0, "2763fe4f0cb74df716ec8b0c464b0988", 1217428},
+ {"resource.005", 0, "d608713197c5ba1cd8c6ed46299c3069", 1057924},
+ AD_LISTEND},
+ Common::RU_RUS, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Space Quest 5 - English DOS (from the Space Quest Collection)
// Executable scanning reports "1.001.068", VERSION file reports "1.04"
{"sq5", "", {
@@ -3376,6 +3466,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Space Quest 5 - Spanish DOS Floppy (from mirir, bug report #3090664)
+ {"sq5", "", {
+ {"resource.000", 0, "73748852548faa42927f7537b165582d", 6049994},
+ {"resource.map", 0, "5714a899033bdebf2d61ad333c8c6637", 6492},
+ {"resource.msg", 0, "46deca7ef9cf057f7d442df98c1a2ae2", 134612},
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Space Quest 5 - Russian DOS
+ // Executable scanning reports "1.001.068", VERSION file reports "1.994"
+ {"sq5", "", {
+ {"resource.map", 0, "82e6e9b4270a4007578a119b6a51860c", 6493},
+ {"resource.000", 0, "6f9ed21e1001526b4137f6703ed476af", 6103778},
+ {"resource.msg", 0, "0a8931990cd2eac1691602391c68ab85", 147580},
+ AD_LISTEND},
+ Common::RU_RUS, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
#ifdef ENABLE_SCI32
// Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -3523,6 +3630,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
FANMADE("SCI Studio Template 3.0", "ca0dc8d586e0a8670b7621cde090b532", 354, "58a48ee692a86c0575e6bd0b00a92b9a", 113097),
FANMADE("SCI Quest", "9067e1f1e54436d2dbfce855524bc84a", 552, "ffa7d355cd9223f245289108a696bcd2", 149634),
FANMADE("SCI-Man", "3ab85bd39a86c11f85781764f9db09bb", 468, "bb8f9992f504a242bf0860e3588e150b", 131810),
+ FANMADE("The Black Cauldron", "5e1ff2833c7f33ebcfa456ba836e2067", 2592, "2f8e6264d2db91bb54982ab8aa18b3b4", 1881839),
FANMADE("The Farm Nightmare", "fb6cbfddaa7c055e2c3d8cf4c683a7db", 906, "50655e8b8925f717e698e08f006f40be", 338303),
FANMADE("The Gem Scenario", "ef5f61f4d2c6d31122d3e2baf89ad976", 642, "2f16be390dd90c3d7ca1c8a594ac0bfa", 244794),
FANMADE("The Legend of the Lost Jewel", "ba1bca315e3818c5626eda51bcfbcccf", 636, "9b0736d69924af0cff32a0f78db96855", 300398),
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index 97eec38caf..206624f87e 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -40,16 +40,16 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan)
_gfxFunctionsType = SCI_VERSION_NONE;
_messageFunctionType = SCI_VERSION_NONE;
_moveCountType = kMoveCountUninitialized;
-
#ifdef ENABLE_SCI32
_sci21KernelType = SCI_VERSION_NONE;
+ _sci2StringFunctionType = kSci2StringFunctionUninitialized;
#endif
_usesCdTrack = Common::File::exists("cdaudio.map");
}
reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc, int methodNum) {
// Get address of target object
- reg_t objAddr = _segMan->findObjectByName(objName);
+ reg_t objAddr = _segMan->findObjectByName(objName, 0);
reg_t addr;
if (objAddr.isNull()) {
@@ -270,11 +270,20 @@ SciVersion GameFeatures::detectLofsType() {
return _lofsType;
}
- if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
+ // SCI1.1 type, i.e. we compensate for the fact that the heap is attached
+ // to the end of the script
_lofsType = SCI_VERSION_1_1;
return _lofsType;
}
+ if (getSciVersion() == SCI_VERSION_3) {
+ // SCI3 type, same as pre-SCI1.1, really, as there is no separate heap
+ // resource
+ _lofsType = SCI_VERSION_3;
+ return _lofsType;
+ }
+
// Find a function of the "Game" object (which is the game super class) which invokes lofsa/lofss
reg_t gameSuperClass = g_sci->getGameSuperClassAddress();
bool found = false;
@@ -519,6 +528,65 @@ SciVersion GameFeatures::detectSci21KernelType() {
}
return _sci21KernelType;
}
+
+Sci2StringFunctionType GameFeatures::detectSci2StringFunctionType() {
+ if (_sci2StringFunctionType == kSci2StringFunctionUninitialized) {
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ error("detectSci21StringFunctionType() called from SCI1.1 or earlier");
+ } else if (getSciVersion() == SCI_VERSION_2) {
+ // SCI2 games are always using the old type
+ _sci2StringFunctionType = kSci2StringFunctionOld;
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ // SCI3 games are always using the new type
+ _sci2StringFunctionType = kSci2StringFunctionNew;
+ } else { // SCI2.1
+ if (!autoDetectSci21StringFunctionType())
+ _sci2StringFunctionType = kSci2StringFunctionOld;
+ else
+ _sci2StringFunctionType = kSci2StringFunctionNew;
+ }
+ }
+
+ debugC(1, kDebugLevelVM, "Detected SCI2 kString type: %s", (_sci2StringFunctionType == kSci2StringFunctionOld) ? "old" : "new");
+
+ return _sci2StringFunctionType;
+}
+
+bool GameFeatures::autoDetectSci21StringFunctionType() {
+ // Look up the script address
+ reg_t addr = getDetectionAddr("Str", SELECTOR(size));
+
+ if (!addr.segment)
+ return false;
+
+ uint16 offset = addr.offset;
+ Script *script = _segMan->getScript(addr.segment);
+
+ while (true) {
+ int16 opparams[4];
+ byte extOpcode;
+ byte opcode;
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
+ opcode = extOpcode >> 1;
+
+ // Check for end of script
+ if (opcode == op_ret || offset >= script->getBufSize())
+ break;
+
+ if (opcode == op_callk) {
+ uint16 kFuncNum = opparams[0];
+
+ // SCI2.1 games which use the new kString functions call kString(8).
+ // Earlier ones call the callKernel script function, but not kString
+ // directly
+ if (_kernel->getKernelName(kFuncNum) == "String")
+ return true;
+ }
+ }
+
+ return false; // not found a call to kString
+}
+
#endif
bool GameFeatures::autoDetectMoveCountType() {
@@ -564,6 +632,9 @@ MoveCountType GameFeatures::detectMoveCountType() {
// SCI0/SCI01 games always increment move count
if (getSciVersion() <= SCI_VERSION_01) {
_moveCountType = kIncrementMoveCount;
+ } else if (getSciVersion() >= SCI_VERSION_1_1) {
+ // SCI1.1 and newer games always ignore move count
+ _moveCountType = kIgnoreMoveCount;
} else {
if (!autoDetectMoveCountType()) {
error("Move count autodetection failed");
@@ -577,4 +648,17 @@ MoveCountType GameFeatures::detectMoveCountType() {
return _moveCountType;
}
+bool GameFeatures::useAltWinGMSound() {
+ if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD()) {
+ SciGameId id = g_sci->getGameId();
+ return (id == GID_ECOQUEST ||
+ id == GID_JONES ||
+ id == GID_KQ5 ||
+ //id == GID_FREDDYPHARKAS || // Has alternate tracks, but handles them differently
+ id == GID_SQ4);
+ } else {
+ return false;
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
index 755054fb25..8237d43714 100644
--- a/engines/sci/engine/features.h
+++ b/engines/sci/engine/features.h
@@ -37,6 +37,12 @@ enum MoveCountType {
kIncrementMoveCount
};
+enum Sci2StringFunctionType {
+ kSci2StringFunctionUninitialized,
+ kSci2StringFunctionOld,
+ kSci2StringFunctionNew
+};
+
class GameFeatures {
public:
GameFeatures(SegManager *segMan, Kernel *kernel);
@@ -57,7 +63,7 @@ public:
/**
* Autodetects the Lofs type
- * @return Lofs type, SCI_VERSION_0_EARLY / SCI_VERSION_1_MIDDLE / SCI_VERSION_1_1
+ * @return Lofs type, SCI_VERSION_0_EARLY / SCI_VERSION_1_MIDDLE / SCI_VERSION_1_1 / SCI_VERSION_3
*/
SciVersion detectLofsType();
@@ -79,6 +85,13 @@ public:
* @return Graphics functions type, SCI_VERSION_2 / SCI_VERSION_2_1
*/
SciVersion detectSci21KernelType();
+
+ /**
+ * Autodetects the string subfunctions used in SCI2 - SCI3
+ * @return string subfunctions type, kSci2StringFunctionOld / kSci2StringFunctionNew
+ */
+ Sci2StringFunctionType detectSci2StringFunctionType();
+
#endif
/**
@@ -100,6 +113,13 @@ public:
bool usesCdTrack() { return _usesCdTrack; }
+ /**
+ * Checks if the alternative Windows GM MIDI soundtrack should be used. Such
+ * soundtracks are available for the Windows CD versions of EcoQuest, Jones,
+ * KQ5 and SQ4.
+ */
+ bool useAltWinGMSound();
+
private:
reg_t getDetectionAddr(const Common::String &objName, Selector slc, int methodNum = -1);
@@ -109,11 +129,13 @@ private:
bool autoDetectMoveCountType();
#ifdef ENABLE_SCI32
bool autoDetectSci21KernelType();
+ bool autoDetectSci21StringFunctionType();
#endif
SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType, _messageFunctionType;
#ifdef ENABLE_SCI32
SciVersion _sci21KernelType;
+ Sci2StringFunctionType _sci2StringFunctionType;
#endif
MoveCountType _moveCountType;
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index ff327a0049..16534fbce9 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -66,12 +66,12 @@ const Common::String &Kernel::getSelectorName(uint selector) {
// We need this for proper workaround tables
// TODO: maybe check, if there is a fixed selector-table and error() out in that case
for (uint loopSelector = _selectorNames.size(); loopSelector <= selector; ++loopSelector)
- _selectorNames.push_back(Common::String::printf("<noname%d>", loopSelector));
+ _selectorNames.push_back(Common::String::format("<noname%d>", loopSelector));
}
// Ensure that the selector has a name
if (_selectorNames[selector].empty())
- _selectorNames[selector] = Common::String::printf("<noname%d>", selector);
+ _selectorNames[selector] = Common::String::format("<noname%d>", selector);
return _selectorNames[selector];
}
@@ -88,6 +88,14 @@ const Common::String &Kernel::getKernelName(uint number) const {
return _kernelNames[number];
}
+int Kernel::findKernelFuncPos(Common::String kernelFuncName) {
+ for (uint32 i = 0; i < _kernelNames.size(); i++)
+ if (_kernelNames[i] == kernelFuncName)
+ return i;
+
+ return -1;
+}
+
int Kernel::findSelector(const char *selectorName) const {
for (uint pos = 0; pos < _selectorNames.size(); ++pos) {
if (_selectorNames[pos] == selectorName)
@@ -130,7 +138,7 @@ void Kernel::loadSelectorNames() {
Common::String tmp((const char *)r->data + offset + 2, len);
_selectorNames.push_back(tmp);
- //printf("%s\n", tmp.c_str()); // debug
+ //debug("%s", tmp.c_str());
// Early SCI versions used the LSB in the selector ID as a read/write
// toggle. To compensate for that, we add every selector name twice.
@@ -422,8 +430,8 @@ static void kernelSignatureDebugType(const uint16 type) {
while (list->typeCheck) {
if (type & list->typeCheck) {
if (!firstPrint)
- printf(", ");
- printf("%s", list->text);
+ debugN(", ");
+ debugN("%s", list->text);
firstPrint = false;
}
list++;
@@ -434,38 +442,38 @@ static void kernelSignatureDebugType(const uint16 type) {
void Kernel::signatureDebug(const uint16 *sig, int argc, const reg_t *argv) {
int argnr = 0;
while (*sig || argc) {
- printf("parameter %d: ", argnr++);
+ debugN("parameter %d: ", argnr++);
if (argc) {
reg_t parameter = *argv;
- printf("%04x:%04x (", PRINT_REG(parameter));
+ debugN("%04x:%04x (", PRINT_REG(parameter));
int regType = findRegType(parameter);
if (regType)
kernelSignatureDebugType(regType);
else
- printf("unknown type of %04x:%04x", PRINT_REG(parameter));
- printf(")");
+ debugN("unknown type of %04x:%04x", PRINT_REG(parameter));
+ debugN(")");
argv++;
argc--;
} else {
- printf("not passed");
+ debugN("not passed");
}
if (*sig) {
const uint16 signature = *sig;
if ((signature & SIG_MAYBE_ANY) == SIG_MAYBE_ANY) {
- printf(", may be any");
+ debugN(", may be any");
} else {
- printf(", should be ");
+ debugN(", should be ");
kernelSignatureDebugType(signature);
}
if (signature & SIG_IS_OPTIONAL)
- printf(" (optional)");
+ debugN(" (optional)");
if (signature & SIG_NEEDS_MORE)
- printf(" (needs more)");
+ debugN(" (needs more)");
if (signature & SIG_MORE_MAY_FOLLOW)
- printf(" (more may follow)");
+ debugN(" (more may follow)");
sig++;
}
- printf("\n");
+ debugN("\n");
}
}
@@ -828,7 +836,7 @@ void Kernel::setKernelNamesSci21(GameFeatures *features) {
// version (2.100.002), yet they would not be compatible with other
// games of the same interpreter.
- if (features->detectSci21KernelType() == SCI_VERSION_2) {
+ if (getSciVersion() != SCI_VERSION_3 && features->detectSci21KernelType() == SCI_VERSION_2) {
_kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
// OnMe is IsOnMe here, but they should be compatible
_kernelNames[0x23] = "Robot"; // Graph in SCI2
@@ -903,6 +911,13 @@ void script_adjust_opcode_formats() {
g_opcode_formats[op_call][1] = Script_Word;
g_opcode_formats[op_callb][1] = Script_Word;
}
+
+ if (getSciVersion() >= SCI_VERSION_3) {
+ // TODO: There are also opcodes in
+ // here to get the superclass, and possibly the species too.
+ g_opcode_formats[0x4d/2][0] = Script_None;
+ g_opcode_formats[0x4e/2][0] = Script_None;
+ }
#endif
}
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index e50c6aaae2..58a6181082 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -151,6 +151,7 @@ public:
uint getSelectorNamesSize() const;
const Common::String &getSelectorName(uint selector);
+ int findKernelFuncPos(Common::String kernelFuncName);
uint getKernelNamesSize() const;
const Common::String &getKernelName(uint number) const;
@@ -469,6 +470,7 @@ reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv);
reg_t kWinHelp(EngineState *s, int argc, reg_t *argv);
reg_t kWinDLL(EngineState *s, int argc, reg_t *argv);
reg_t kPrintDebug(EngineState *s, int argc, reg_t *argv);
+reg_t kGetConfig(EngineState *s, int argc, reg_t *argv);
#endif
reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
@@ -486,7 +488,7 @@ reg_t kDoSoundFade(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundGetPolyphony(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv);
-reg_t kDoSoundReverb(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundGlobalReverb(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundSetHold(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundDummy(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index d2c95053d5..99c1e079df 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -27,6 +27,7 @@
#define SCI_ENGINE_KERNEL_TABLES_H
#include "sci/engine/workarounds.h"
+#include "sci/engine/vm.h" // for opcode_formats
namespace Sci {
@@ -58,7 +59,7 @@ struct SciKernelMapSubEntry {
#define SIG_SCI1 SCI_VERSION_1_EGA, 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_SCI21 SCI_VERSION_2_1, SCI_VERSION_2_1
+#define SIG_SCI21 SCI_VERSION_2_1, SCI_VERSION_3
#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
@@ -67,7 +68,7 @@ struct SciKernelMapSubEntry {
#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE
#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY
#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE
-#define SIG_SOUNDSCI21 SCI_VERSION_2_1, SCI_VERSION_2_1
+#define SIG_SOUNDSCI21 SCI_VERSION_2_1, SCI_VERSION_3
#define SIGFOR_ALL 0x3f
#define SIGFOR_DOS 1 << 0
@@ -86,192 +87,192 @@ struct SciKernelMapSubEntry {
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kDoSound_subops[] = {
- { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL },
- { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL },
- { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundRestore), "(o)", NULL },
- { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL },
- { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL },
- { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL },
- { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
- { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL },
- { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
- { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
- { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds },
- { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
- { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL },
- { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
- // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and
- // playBed. The methods are the same, apart from the second integer parameter: it's 0 in
- // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what
- // it actually does internally
- { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
- { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds },
- { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
- { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL },
- { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "i", NULL },
- { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
- { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL },
- // ^^ Longbow demo
- { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundRestore), "", NULL },
- { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
- { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
- { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds },
- { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL },
- { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL },
+ { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL },
+ { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundRestore), "(o)", NULL },
+ { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL },
+ { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL },
+ { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL },
+ { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
+ { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL },
+ { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
+ { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
+ { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
+ { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL },
+ { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
+ // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and
+ // playBed. The methods are the same, apart from the second integer parameter: it's 0 in
+ // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what
+ // it actually does internally
+ { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
+ { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
+ { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundGlobalReverb), "(i)", NULL },
+ { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL },
+ // ^^ Longbow demo
+ { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundRestore), "", NULL },
+ { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
+ { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
+ { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL },
+ { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundGlobalReverb), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
#ifdef ENABLE_SCI32
- { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
- { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL },
- { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
- { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL },
- { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL },
- { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL },
- // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the
- // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although
- // I guess there are many more changes somewhere
- // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly
- // signature for SCI21 should be "o"
- { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL },
- { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL },
- { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
- { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
- { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL },
- { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL },
- { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL },
- { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
- { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
- { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
- { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
+ { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL },
+ { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL },
+ { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL },
+ // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the
+ // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although
+ // I guess there are many more changes somewhere
+ // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly
+ // signature for SCI21 should be "o"
+ { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL },
+ { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL },
+ { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL },
+ { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL },
+ { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
+ { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundGlobalReverb), NULL, NULL },
+ { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
#endif
- SCI_SUBOPENTRY_TERMINATOR
+ SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kGraph_subops[] = {
- { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start
- { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
- // 3 - set palette via resource
- { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds },
- // 5 - nop
- // 6 - draw pattern
- { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds },
- { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
- // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
- { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
- { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
- { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
- { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", kGraphUpdateBox_workarounds }, // kq6 hires
- { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", kGraphUpdateBox_workarounds },
- { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
- { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
- { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start
+ { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
+ // 3 - set palette via resource
+ { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds },
+ // 5 - nop
+ // 6 - draw pattern
+ { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds },
+ { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
+ // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
+ { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
+ { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
+ { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", kGraphUpdateBox_workarounds }, // kq6 hires
+ { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", kGraphUpdateBox_workarounds },
+ { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
+ { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
+ { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
+ SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalVary_subops[] = {
- { SIG_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL },
- { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
- { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
- { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
- { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
- { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
- { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
- { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
- { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
+ { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalette_subops[] = {
- { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
- { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
- { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
- { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
- { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
- { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
- { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
- { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
+ { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
static const SciKernelMapSubEntry kFileIO_subops[] = {
- { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL },
- { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL },
- { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL },
- { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL },
- { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL },
- { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL },
- { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL },
- { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL },
- { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL },
- { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL },
- { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL },
- { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL },
- { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL },
+ { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL },
+ { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL },
+ { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL },
#ifdef ENABLE_SCI32
- { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL },
- { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
- { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
- { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
- { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
+ { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL },
+ { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
+ { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
+ { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
+ { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
#endif
- SCI_SUBOPENTRY_TERMINATOR
+ SCI_SUBOPENTRY_TERMINATOR
};
#ifdef ENABLE_SCI32
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
- { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
- { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
- { SIG_SCI21, 2, MAP_CALL(NewNode), ".(.)", NULL },
- { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL },
- { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL },
- { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL },
- { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
- { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
- { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
- { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
- { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
- { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln(.)", NULL },
- { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
- { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
- { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL },
- { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL },
- { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL },
- { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL },
- { SIG_SCI21, 18, MAP_CALL(ListIndexOf) , "l[io]", NULL },
- { SIG_SCI21, 19, MAP_CALL(ListEachElementDo), "li(.*)", NULL },
- { SIG_SCI21, 20, MAP_CALL(ListFirstTrue), "li(.*)", NULL },
- { SIG_SCI21, 21, MAP_CALL(ListAllTrue), "li(.*)", NULL },
- { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL },
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
+ { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
+ { SIG_SCI21, 2, MAP_CALL(NewNode), ".(.)", NULL },
+ { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL },
+ { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL },
+ { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL },
+ { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
+ { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
+ { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
+ { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
+ { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
+ { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln(.)", NULL },
+ { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
+ { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
+ { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL },
+ { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL },
+ { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL },
+ { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL },
+ { SIG_SCI21, 18, MAP_CALL(ListIndexOf) , "l[io]", NULL },
+ { SIG_SCI21, 19, MAP_CALL(ListEachElementDo), "li(.*)", NULL },
+ { SIG_SCI21, 20, MAP_CALL(ListFirstTrue), "li(.*)", NULL },
+ { SIG_SCI21, 21, MAP_CALL(ListAllTrue), "li(.*)", NULL },
+ { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
#endif
@@ -290,251 +291,303 @@ struct SciKernelMapEntry {
// name, version/platform, signature, sub-signatures, workarounds
static SciKernelMapEntry s_kernelMap[] = {
- { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
- { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
- { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
- { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
- { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
- { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
- { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
- { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
- { 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(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
+ { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
+ { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
+ { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
+ { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
+ { 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 },
#ifdef ENABLE_SCI32
- { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
+ { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", 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 },
- { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
- { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
- { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
- { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, kDeviceInfo_workarounds }, // subop
- { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, kDisplay_workarounds },
- // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro
- // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same
- { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, kDirLoop_workarounds },
- { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds },
- { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL },
- { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DoSound), SIG_EVERYWHERE, "i(.*)", kDoSound_subops, NULL },
- { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires
- { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
- { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
- { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
- { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
- { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
- { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
- { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
- { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
- { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
- { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
- { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds },
- { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
- { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
- { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
- { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
- { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
- { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
- { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
- { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
- { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
- { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
- { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds },
- { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
- { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
- { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
- { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
- { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds },
- { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
- { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
- { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
- { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
- { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
- { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
- { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
- { 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, NULL },
- { MAP_CALL(RemapColors), SIG_EVERYWHERE, "i(i)(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
- { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
- { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL },
- { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
- { MAP_CALL(SaveGame), SIG_EVERYWHERE, "[r0]i[r0](r)", NULL, NULL },
- { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL },
- // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why
- { MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
- { 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(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 },
- { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
- { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Show), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL },
- { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, kStrAt_workarounds },
- { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, kStrCat_workarounds },
- { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL },
- { MAP_CALL(StrCpy), SIG_EVERYWHERE, "r[r0](i)", NULL, NULL },
- { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
- { 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(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
- { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
- { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
- { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
+ { 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 },
+ { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
+ { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
+ { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, kDeviceInfo_workarounds }, // subop
+ { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, kDisplay_workarounds },
+ // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro
+ // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same
+ { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, kDirLoop_workarounds },
+ { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds },
+ { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoSound), SIG_EVERYWHERE, "i(.*)", kDoSound_subops, NULL },
+ { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires
+ { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
+ { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
+ { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
+ { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
+ { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
+ { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
+ { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
+ { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
+ { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
+ { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds },
+ { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
+ { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
+ { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
+ { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
+ { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
+ { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
+ { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
+ { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
+ { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds },
+ { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
+ { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
+ { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
+ { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds },
+ { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
+ { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
+ { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
+ { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
+ { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { 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, NULL },
+ { MAP_CALL(RemapColors), SIG_EVERYWHERE, "i(i)(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
+ { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
+ { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL },
+ { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(SaveGame), SIG_EVERYWHERE, "[r0]i[r0](r)", NULL, NULL },
+ { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL },
+ // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why
+ { MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+ { 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(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 },
+ { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
+ { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Show), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL },
+ { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, kStrAt_workarounds },
+ { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, kStrCat_workarounds },
+ { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL },
+ { MAP_CALL(StrCpy), SIG_EVERYWHERE, "r[r0](i)", NULL, NULL },
+ { 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(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
+ { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
- // Unimplemented SCI0-SCI1.1 unused functions, always mapped to kDummy
- { MAP_DUMMY(InspectObj), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(ShowSends), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(ShowObjs), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(ShowFree), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(StackUsage), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(Profiler), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(ShiftScreen), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(ListOps), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(ATan), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(Record), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(PlayBack), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(DbugStr), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // Unimplemented SCI0-SCI1.1 unused functions, always mapped to kDummy
+ { MAP_DUMMY(InspectObj), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowSends), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowObjs), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowFree), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(StackUsage), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(Profiler), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShiftScreen), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ListOps), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ATan), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(Record), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(PlayBack), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(DbugStr), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- // =======================================================================================================
+ // =======================================================================================================
#ifdef ENABLE_SCI32
- // SCI2 Kernel Functions
- // TODO: whoever knows his way through those calls, fix the signatures.
- { 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(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
- { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
- { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL },
- { "OnMe", kIsOnMe, SIG_EVERYWHERE, "iioi", NULL, NULL },
- { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii(i)", NULL, NULL },
- { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ // SCI2 Kernel Functions
+ // TODO: whoever knows his way through those calls, fix the signatures.
+ { 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(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
+ { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
+ { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL },
+ { "OnMe", kIsOnMe, SIG_EVERYWHERE, "iioi", NULL, NULL },
+ { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii([ri])(i)", NULL, NULL },
+ { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
- // SCI2 empty functions
+ // SCI2 unmapped functions - TODO!
+ // SetScroll
+ // AddMagnify // most probably similar to the SCI1.1 functions. We need a test case
+ // DeleteMagnify
+ // EditText
+ // DisposeTextBitmap
+ // VibrateMouse - used in QFG4 floppy
+ // PalCycle
+ // ObjectIntersect - used in QFG4 floppy
- // Purge is used by the memory manager in SSCI to ensure that X number of bytes (the so called "unmovable
- // memory") are available. We have our own memory manager and garbage collector, thus we ignore this call.
- { MAP_EMPTY(Purge), SIG_EVERYWHERE, "i", NULL, NULL },
+ // SCI2 empty functions
- // SCI2.1 Kernel Functions
- { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", NULL, NULL },
- { MAP_CALL(List), SIG_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
- { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
- { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, 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(WinDLL), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(PrintDebug), SIG_EVERYWHERE, "ri", NULL, NULL },
+ // Purge is used by the memory manager in SSCI to ensure that X number of bytes (the so called "unmovable
+ // memory") are available. We have our own memory manager and garbage collector, thus we ignore this call.
+ { MAP_EMPTY(Purge), SIG_EVERYWHERE, "i", NULL, NULL },
- // SCI2.1 empty functions
+ // Unused SCI2 unused functions, always mapped to kDummy
+ { MAP_DUMMY(InspectObject), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // Profiler (same as SCI0-SCI1.1)
+ // Record (same as SCI0-SCI1.1)
+ // PlayBack (same as SCI0-SCI1.1)
+ { MAP_DUMMY(MonoOut), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(SetFatalStr), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(IntegrityChecking),SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(CheckIntegrity), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { 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(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(MakeSaveCatName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(MakeSaveFileName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- // SetWindowsOption is used to set Windows specific options, like for example the title bar visibility of
- // the game window in Phantasmagoria 2. We ignore these settings completely.
- { MAP_EMPTY(SetWindowsOption), SIG_EVERYWHERE, "ii", NULL, NULL },
+ // SCI2.1 Kernel Functions
+ { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", NULL, NULL },
+ { MAP_CALL(List), SIG_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
+ { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
+ { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, 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(WinDLL), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(GetConfig), SIG_EVERYWHERE, "ro", NULL, NULL },
+// Commented out because it needs to be implemented in full generality in Shivers/Full.
+// Since it is not essential to game play, removing it also works.
+//
+// { MAP_CALL(PrintDebug), SIG_EVERYWHERE, "ri", NULL, NULL },
- // Unimplemented SCI2.1 unused functions, always mapped to kDummy
- { MAP_DUMMY(InspectObject), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- // Profiler (same as SCI0-SCI1.1)
- // Record (same as SCI0-SCI1.1)
- // PlayBack (same as SCI0-SCI1.1)
- { MAP_DUMMY(MonoOut), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(SetFatalStr), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(IntegrityChecking),SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(CheckIntegrity), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(MarkMemory), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(GetSierraProfileString), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // SCI2.1 unmapped functions - TODO!
+ // SetLanguage
+ // FindSelector
+ // FindClass
+ // CelRect
+ // BaseLineSpan
+ // CelInfo
+ // Bitmap
+ // CelLink
+ // MovePlaneItems
+ // Font
+ // ScrollWindow
+ // AddLine
+ // DeleteLine
+ // UpdateLine
+ // AddPolygon
+ // DeletePolygon
+ // UpdatePolygon
+ // GetConfig
+ // Table
+ // LoadChunk
+ // SetPalStyleRange
+ // NewRoom
+ // Priority
+ // MorphOn
+ // SetHotRectangles
+ // DeletePic
+
+ // SCI2.1 empty functions
+
+ // SetWindowsOption is used to set Windows specific options, like for example the title bar visibility of
+ // the game window in Phantasmagoria 2. We ignore these settings completely.
+ { MAP_EMPTY(SetWindowsOption), SIG_EVERYWHERE, "ii", NULL, NULL },
+
+ // Unused SCI2.1 unused functions, always mapped to kDummy
+ { MAP_DUMMY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(GetSierraProfileString), SIG_EVERYWHERE, "(.*)", NULL, NULL },
#endif
- { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
+ { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
};
/** Default kernel name table. */
@@ -724,7 +777,7 @@ static const char *sci2_default_knames[] = {
/*0x21*/ "DeleteMagnify",
/*0x22*/ "IsHiRes",
/*0x23*/ "Graph",
- /*0x24*/ "InvertRect",
+ /*0x24*/ "InvertRect", // only in SCI2, not used in any SCI2 game
/*0x25*/ "TextSize",
/*0x26*/ "Message",
/*0x27*/ "TextColors",
@@ -746,8 +799,8 @@ static const char *sci2_default_knames[] = {
/*0x37*/ "RestoreGame",
/*0x38*/ "RestartGame",
/*0x39*/ "GameIsRestarting",
- /*0x3a*/ "MakeSaveCatName",
- /*0x3b*/ "MakeSaveFileName",
+ /*0x3a*/ "MakeSaveCatName", // only in SCI2, not used in any SCI2 game
+ /*0x3b*/ "MakeSaveFileName", // only in SCI2, not used in any SCI2 game
/*0x3c*/ "GetSaveFiles",
/*0x3d*/ "GetSaveDir",
/*0x3e*/ "CheckSaveGame",
@@ -825,8 +878,8 @@ static const char *sci2_default_knames[] = {
/*0x86*/ "CheckIntegrity", // for debugging
/*0x87*/ "ObjectIntersect",
/*0x88*/ "MarkMemory", // for debugging
- /*0x89*/ "TextWidth",
- /*0x8a*/ "PointSize",
+ /*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",
@@ -929,7 +982,7 @@ static const char *sci21_default_knames[] = {
/*0x49*/ "Font",
/*0x4a*/ "EditText",
/*0x4b*/ "InputText",
- /*0x4c*/ "ScrollWindow",
+ /*0x4c*/ "ScrollWindow", // Dummy in SCI3
/*0x4d*/ "Dummy",
/*0x4e*/ "Dummy",
/*0x4f*/ "Dummy",
@@ -939,7 +992,7 @@ static const char *sci21_default_knames[] = {
/*0x53*/ "MapKeyToDir",
/*0x54*/ "HaveMouse",
/*0x55*/ "SetCursor",
- /*0x56*/ "VibrateMouse",
+ /*0x56*/ "VibrateMouse", // Dummy in SCI3
/*0x57*/ "Dummy",
/*0x58*/ "Dummy",
/*0x59*/ "Dummy",
@@ -980,7 +1033,7 @@ static const char *sci21_default_knames[] = {
/*0x7c*/ "SetQuitStr",
/*0x7d*/ "GetConfig",
/*0x7e*/ "Table",
- /*0x7f*/ "WinHelp", // Windows only
+ /*0x7f*/ "WinHelp", // Windows only
/*0x80*/ "Dummy",
/*0x81*/ "Dummy",
/*0x82*/ "Dummy",
@@ -994,7 +1047,7 @@ static const char *sci21_default_knames[] = {
/*0x8a*/ "LoadChunk",
/*0x8b*/ "SetPalStyleRange",
/*0x8c*/ "AddPicAt",
- /*0x8d*/ "Dummy",
+ /*0x8d*/ "MessageBox", // SCI3, was Dummy in SCI2.1
/*0x8e*/ "NewRoom",
/*0x8f*/ "Dummy",
/*0x90*/ "Priority",
@@ -1008,8 +1061,13 @@ static const char *sci21_default_knames[] = {
/*0x98*/ "GetWindowsOption", // Windows only
/*0x99*/ "WinDLL", // Windows only
/*0x9a*/ "Dummy",
- /*0x9b*/ "Dummy",
- /*0x9c*/ "DeletePic"
+ /*0x9b*/ "Minimize", // SCI3, was Dummy in SCI2.1
+ /*0x9c*/ "DeletePic",
+ // == SCI3 only ===============
+ /*0x9d*/ "Dummy",
+ /*0x9e*/ "WebConnect",
+ /*0x9f*/ "Dummy",
+ /*0xa0*/ "PlayDuck"
};
#endif
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index b6d67513d2..db8d7ec331 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -422,7 +422,7 @@ static void listSavegames(Common::Array<SavegameDesc> &saves) {
Common::SeekableReadStream *in;
if ((in = saveFileMan->openForLoading(filename))) {
SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) {
+ if (!get_savegame_metadata(in, &meta) || meta.name.empty()) {
// invalid
delete in;
continue;
@@ -431,17 +431,17 @@ static void listSavegames(Common::Array<SavegameDesc> &saves) {
SavegameDesc desc;
desc.id = strtol(filename.end() - 3, NULL, 10);
- desc.date = meta.savegame_date;
+ desc.date = meta.saveDate;
// We need to fix date in here, because we save DDMMYYYY instead of
// YYYYMMDD, so sorting wouldn't work
desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
- desc.time = meta.savegame_time;
- desc.version = meta.savegame_version;
+ desc.time = meta.saveTime;
+ desc.version = meta.version;
- if (meta.savegame_name.lastChar() == '\n')
- meta.savegame_name.deleteLastChar();
+ if (meta.name.lastChar() == '\n')
+ meta.name.deleteLastChar();
- Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH);
+ Common::strlcpy(desc.name, meta.name.c_str(), SCI_MAX_SAVENAME_LENGTH);
debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
@@ -584,7 +584,7 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
g_system->getTimeAndDate(curTime);
curTime.tm_year += 1900; // fixup year
curTime.tm_mon++; // fixup month
- game_description = Common::String::printf("%02d.%02d.%04d / %02d:%02d:%02d", curTime.tm_mday, curTime.tm_mon, curTime.tm_year, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ game_description = Common::String::format("%02d.%02d.%04d / %02d:%02d:%02d", curTime.tm_mday, curTime.tm_mon, curTime.tm_year, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
}
delete dialog;
g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
@@ -1130,7 +1130,7 @@ reg_t kCD(EngineState *s, int argc, reg_t *argv) {
reg_t kSave(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 0: // Called by kq7 when starting chapters
- return SIGNAL_REG;
+ return kSaveGame(s, argc - 1,argv + 1);
case 2: // GetSaveDir
// Yay! Reusing the old kernel function!
return kGetSaveDir(s, argc - 1, argv + 1);
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 07385fd3f9..33c819fa4c 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -33,6 +33,7 @@
#include "sci/sci.h"
#include "sci/debug.h" // for g_debug_sleeptime_factor
+#include "sci/event.h"
#include "sci/resource.h"
#include "sci/engine/features.h"
#include "sci/engine/state.h"
@@ -256,10 +257,11 @@ reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
int16 control = (argc > 6) ? argv[6].toSint16() : -1;
- // TODO: Find out why we get > 15 for color in EGA
- // FIXME: EGA? Which EGA? SCI0 or SCI1? Check the
- // workarounds inside kGraphFillBoxAny and kNewWindow
- if (!g_sci->getResMan()->isVGA() && !g_sci->getResMan()->isAmiga32color())
+ // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15).
+ // Colors above 15 are all white in SCI1 EGA games, which is why this was never
+ // observed. We clip them all to (0, 15) instead, as colors above 15 are used
+ // for the undithering algorithm in EGA games - bug #3048908.
+ if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY)
color &= 0x0F;
g_sci->_gfxPaint16->kernelGraphDrawLine(getGraphPoint(argv), getGraphPoint(argv + 2), color, priority, control);
@@ -297,7 +299,7 @@ reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) {
int16 priority = argv[6].toSint16(); // yes, we may read from stack sometimes here
int16 control = argv[7].toSint16(); // sierra did the same
- // WORKAROUND: PQ3 EGA is setting invalid colors (above 0 - 15).
+ // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15).
// Colors above 15 are all white in SCI1 EGA games, which is why this was never
// observed. We clip them all to (0, 15) instead, as colors above 15 are used
// for the undithering algorithm in EGA games - bug #3048908.
@@ -341,6 +343,11 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
int maxwidth = (argc > 3) ? argv[3].toUint16() : 0;
int font_nr = argv[2].toUint16();
+ if (!dest) {
+ debugC(2, kDebugLevelStrings, "GetTextSize: Empty destination");
+ return s->r_acc;
+ }
+
Common::String sep_str;
const char *sep = NULL;
if ((argc > 4) && (argv[4].segment)) {
@@ -350,7 +357,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
dest[0] = dest[1] = NULL_REG;
- if (text.empty() || !dest) { // Empty text
+ if (text.empty()) { // Empty text
dest[2] = dest[3] = make_reg(0, 0);
debugC(2, kDebugLevelStrings, "GetTextSize: Empty string");
return s->r_acc;
@@ -1083,7 +1090,7 @@ reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) {
int colorPen = (argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0;
int colorBack = (argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255;
- // WORKAROUND: PQ3 EGA is setting invalid colors (above 0 - 15).
+ // WORKAROUND: SCI1 EGA games can set invalid colors (above 0 - 15).
// Colors above 15 are all white in SCI1 EGA games, which is why this was never
// observed. We clip them all to (0, 15) instead, as colors above 15 are used
// for the undithering algorithm in EGA games - bug #3048908.
@@ -1112,6 +1119,14 @@ reg_t kAnimate(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxAnimate->kernelAnimate(castListReference, cycle, argc, argv);
+ // WORKAROUND: At the end of Ecoquest 1, during the credits, the game
+ // doesn't call kGetEvent(), so no events are processed (e.g. window
+ // focusing, window moving etc). We poll events for that scene, to
+ // keep ScummVM responsive. Fixes ScummVM "freezing" during the credits,
+ // bug #3101846
+ if (g_sci->getGameId() == GID_ECOQUEST && s->currentRoomNumber() == 680)
+ g_sci->getEventManager()->getSciEvent(SCI_EVENT_PEEK);
+
return s->r_acc;
}
@@ -1254,6 +1269,7 @@ reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
// 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;
@@ -1403,6 +1419,10 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
break;
case 8: // sync
//warning("kRobot(sync), obj %04x:%04x", PRINT_REG(argv[1]));
+ // HACK: Make robots return immediately for now,
+ // otherwise they just hang for a while.
+ // TODO: Replace with proper robot functionality.
+ writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
break;
default:
warning("kRobot(%d)", subop);
@@ -1448,6 +1468,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
kStub(s, argc, argv);
+ // Can be called with 7, 8 or 9 parameters
// showStyle matches the style selector of the associated plane object
uint16 showStyle = argv[0].toUint16(); // 0 - 15
reg_t planeObj = argv[1];
@@ -1464,6 +1485,8 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+ // TODO: Check if the plane is in the list of planes to draw
+
return s->r_acc;
}
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 2188087b8c..329e5cd531 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
@@ -618,7 +619,29 @@ reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv) {
}
reg_t kArray(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
+ uint16 op = argv[0].toUint16();
+
+ // Use kString when accessing strings
+ // This is possible, as strings inherit from arrays
+ // and in this case (type 3) arrays are of type char *.
+ // kString is almost exactly the same as kArray, so
+ // this call is possible
+ // TODO: we need to either merge SCI2 strings and
+ // arrays together, and in the future merge them with
+ // the SCI1 strings and arrays in the segment manager
+ if (op == 0) {
+ // New, check if the target type is 3 (string)
+ if (argv[2].toUint16() == 3)
+ return kString(s, argc, argv);
+ } else {
+ if (s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_STRING ||
+ s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_SYS_STRINGS ||
+ s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_SCRIPT) {
+ return kString(s, argc, argv);
+ }
+ }
+
+ switch (op) {
case 0: { // New
reg_t arrayHandle;
SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle);
@@ -671,13 +694,10 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
return argv[1];
}
case 6: { // Cpy
- if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY ||
- s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) {
- // Happens in the RAMA demo
- warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring");
+ if (argv[1].isNull() || argv[3].isNull()) {
+ warning("kArray(Cpy): Request to copy from or to a null pointer");
return NULL_REG;
}
-
SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
uint32 index1 = argv[2].toUint16();
@@ -700,11 +720,43 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
}
case 7: // Cmp
// Not implemented in SSCI
+ warning("kArray(Cmp) called");
return s->r_acc;
case 8: { // Dup
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ if (argv[1].isNull()) {
+ warning("kArray(Dup): Request to duplicate a null pointer");
+#if 0
+ // Allocate an array anyway
+ reg_t arrayHandle;
+ SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
+ dupArray->setType(3);
+ dupArray->setSize(0);
+ return arrayHandle;
+#endif
+ return NULL_REG;
+ }
+ SegmentType sourceType = s->_segMan->getSegmentObj(argv[1].segment)->getType();
+ if (sourceType == SEG_TYPE_SCRIPT) {
+ // A technique used in later SCI2.1 and SCI3 games: the contents of a script
+ // are loaded in an array (well, actually a string).
+ Script *scr = s->_segMan->getScript(argv[1].segment);
+ reg_t stringHandle;
+
+ SciString *dupString = s->_segMan->allocateString(&stringHandle);
+ dupString->setSize(scr->getBufSize());
+ dupString->fromString(Common::String((const char *)scr->getBuf()));
+
+ return stringHandle;
+ } else if (sourceType != SEG_TYPE_ARRAY && sourceType != SEG_TYPE_SCRIPT) {
+ error("kArray(Dup): Request to duplicate a segment which isn't an array or a script");
+ }
+
reg_t arrayHandle;
SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
+ // This must occur after allocateArray, as inserting a new object
+ // in the heap object list might invalidate this pointer. Also refer
+ // to the same issue in kClone()
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
dupArray->setType(array->getType());
dupArray->setSize(array->getSize());
@@ -720,7 +772,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
return readSelector(s->_segMan, argv[1], SELECTOR(data));
default:
- error("Unknown kArray subop %d", argv[0].toUint16());
+ error("Unknown kArray subop %d", op);
}
return NULL_REG;
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index 792181b832..f90a5b4353 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -126,14 +126,14 @@ reg_t kTimesSin(EngineState *s, int argc, reg_t *argv) {
int angle = argv[0].toSint16();
int factor = argv[1].toSint16();
- return make_reg(0, (int)(factor * sin(angle * PI / 180.0)));
+ return make_reg(0, (int16)(factor * sin(angle * PI / 180.0)));
}
reg_t kTimesCos(EngineState *s, int argc, reg_t *argv) {
int angle = argv[0].toSint16();
int factor = argv[1].toSint16();
- return make_reg(0, (int)(factor * cos(angle * PI / 180.0)));
+ return make_reg(0, (int16)(factor * cos(angle * PI / 180.0)));
}
reg_t kCosDiv(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index d8ae1a3418..90ddf4d7ea 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -75,6 +75,17 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
neededSleep = 60;
}
break;
+ case GID_SQ4:
+ // In SQ4 (floppy and CD) the sequel police appear way too quickly in
+ // the Skate-o-rama rooms, resulting in all sorts of timer issues, like
+ // #3109139 (which occurs because a police officer instantly teleports
+ // just before Roger exits and shoots him). We throttle these scenes a
+ // bit more, in order to prevent timer bugs related to the sequel police
+ if (s->currentRoomNumber() == 405 || s->currentRoomNumber() == 406 ||
+ s->currentRoomNumber() == 410 || s->currentRoomNumber() == 411) {
+ s->_throttleTrigger = true;
+ neededSleep = 60;
+ }
default:
break;
}
@@ -164,7 +175,7 @@ reg_t kFlushResources(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetDebug(EngineState *s, int argc, reg_t *argv) {
- printf("Debug mode activated\n");
+ debug("Debug mode activated");
g_sci->_debugState.seeking = kDebugSeekNothing;
g_sci->_debugState.runningStep = 0;
@@ -180,11 +191,10 @@ enum {
reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
TimeDate loc_time;
- uint32 elapsedTime;
+ uint32 elapsedTime = g_engine->getTotalPlayTime();
int retval = 0; // Avoid spurious warning
g_system->getTimeAndDate(loc_time);
- elapsedTime = g_system->getMillis() - s->gameStartTime;
int mode = (argc > 0) ? argv[0].toUint16() : 0;
@@ -231,14 +241,18 @@ 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: pq3 (multilingual) when plotting crimes - allocates the
- // returned bytes from kStrLen on "W" and "E" and wants to put a
- // string in there, which doesn't fit of course. That's why we allocate
- // one byte more all the time inside that room
- if (g_sci->getGameId() == GID_PQ3) {
- if (s->currentRoomNumber() == 202)
- byteCount++;
- }
+ // WORKAROUND:
+ // - 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
+ // fit of course.
+ // - 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++;
+
if (!s->_segMan->allocDynmem(byteCount, "kMemory() critical", &s->r_acc)) {
error("Critical heap allocation failed");
}
@@ -326,6 +340,17 @@ reg_t kIconBar(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+#ifdef ENABLE_SCI32
+reg_t kGetConfig(EngineState *s, int argc, reg_t *argv) {
+ Common::String setting = s->_segMan->getString(argv[0]);
+ reg_t data = readSelector(s->_segMan, argv[1], SELECTOR(data));
+
+ warning("Get config setting %s", setting.c_str());
+ s->_segMan->strcpy(data, "");
+ return argv[1];
+}
+#endif
+
enum kSciPlatforms {
kSciPlatformDOS = 1,
kSciPlatformWindows = 2
@@ -421,12 +446,12 @@ reg_t kStub(EngineState *s, int argc, reg_t *argv) {
}
Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) +
- Common::String::printf("[%x]", kernelCallNr) +
+ Common::String::format("[%x]", kernelCallNr) +
" invoked. Params: " +
- Common::String::printf("%d", argc) + " (";
+ Common::String::format("%d", argc) + " (";
for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += Common::String::format("%04x:%04x", PRINT_REG(argv[i]));
warningMsg += (i == argc - 1 ? ")" : ", ");
}
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 6a052a582d..0b55c0fce7 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -62,7 +62,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
}
#ifdef DEBUG_PARSER
- printf("Said block: ");
+ debugN("Said block: ");
g_sci->getVocabulary()->debugDecipherSaidBlock(said_block);
#endif
@@ -74,7 +74,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
if (new_lastmatch != SAID_NO_MATCH) { /* Build and possibly display a parse tree */
#ifdef DEBUG_PARSER
- printf("kSaid: Match.\n");
+ debugN("kSaid: Match.\n");
#endif
s->r_acc = make_reg(0, 1);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index faf966af92..2b3b7f8f01 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -938,7 +938,7 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point
// Fall through
case POLY_BARRED_ACCESS:
case POLY_NEAREST_ACCESS:
- if (cont == CONT_INSIDE) {
+ if (cont != CONT_OUTSIDE) {
if (s->_prependPoint != NULL) {
// We shouldn't get here twice.
// We need to break in this case, otherwise we'll end in an infinite
@@ -958,7 +958,8 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point
// The original start position is in an invalid location, so we
// use the moved point and add the original one to the final path
// later on.
- s->_prependPoint = new Common::Point(start);
+ if (start != *new_start)
+ s->_prependPoint = new Common::Point(start);
}
}
@@ -1014,7 +1015,7 @@ static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point &
// For near-point access polygons we need to add the original end point
// to the path after pathfinding.
- if (type == POLY_NEAREST_ACCESS)
+ if ((type == POLY_NEAREST_ACCESS) && (end != *new_end))
s->_appendPoint = new Common::Point(end);
}
}
@@ -1311,6 +1312,8 @@ static void AStar(PathfindingState *s) {
}
}
+ assert(vertex_min != 0); // the vertex cost should never be bigger than HUGE_DISTANCE
+
// Check if we are done
if (vertex_min == s->vertex_end)
break;
@@ -1328,15 +1331,21 @@ static void AStar(PathfindingState *s) {
if (closedSet.contains(vertex))
continue;
- // Avoid plotting path along screen edge
- if ((vertex_min != s->vertex_start) || (vertex != s->vertex_end))
- if (s->pointOnScreenBorder(vertex_min->v) && s->pointOnScreenBorder(vertex->v))
- continue;
-
if (!openSet.contains(vertex))
openSet.push_front(vertex);
new_dist = vertex_min->costG + (uint32)sqrt((float)vertex_min->v.sqrDist(vertex->v));
+
+ // When travelling to a vertex on the screen edge, we
+ // add a penalty score to make this path less appealing.
+ // NOTE: If an obstacle has only one vertex on a screen edge,
+ // later SSCI pathfinders will treat that vertex like any
+ // other, while we apply a penalty to paths traversing it.
+ // This difference might lead to problems, but none are
+ // known at the time of writing.
+ if (s->pointOnScreenBorder(vertex->v))
+ new_dist += 10000;
+
if (new_dist < vertex->costG) {
vertex->costG = new_dist;
vertex->costF = vertex->costG + (uint32)sqrt((float)vertex->v.sqrDist(s->vertex_end->v));
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index e7f466f9a2..4b7451d355 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -103,7 +103,7 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) {
g_sci->getResMan()->unlockResource(which);
else {
if (id.getType() == kResourceTypeInvalid)
- warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), type);
+ warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), argv[0].toUint16());
else
// Happens in CD games (e.g. LSL6CD) with the message
// resource. It isn't fatal, and it's usually caused
@@ -155,7 +155,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelMemory, "Attempting to clone from %04x:%04x", PRINT_REG(parentAddr));
- uint16 infoSelector = readSelectorValue(s->_segMan, parentAddr, SELECTOR(_info_));
+ uint16 infoSelector = parentObj->getInfoSelector().offset;
cloneObj = s->_segMan->allocateClone(&cloneAddr);
if (!cloneObj) {
@@ -174,12 +174,12 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
// extend the internal storage size.
if (infoSelector & kInfoFlagClone)
parentObj = s->_segMan->getObject(parentAddr);
-
+
*cloneObj = *parentObj;
// Mark as clone
infoSelector &= ~kInfoFlagClass; // remove class bit
- writeSelectorValue(s->_segMan, cloneAddr, SELECTOR(_info_), infoSelector | kInfoFlagClone);
+ cloneObj->setInfoSelector(make_reg(0, infoSelector | kInfoFlagClone));
cloneObj->setSpeciesSelector(cloneObj->getPos());
if (parentObj->isClass())
@@ -206,7 +206,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
// At least kq4early relies on this behaviour. The scripts clone "Sound", then set bit 1 manually
// and call kDisposeClone later. In that case we may not free it, otherwise we will run into issues
// later, because kIsObject would then return false and Sound object wouldn't get checked.
- uint16 infoSelector = readSelectorValue(s->_segMan, obj, SELECTOR(_info_));
+ uint16 infoSelector = object->getInfoSelector().offset;
if ((infoSelector & 3) == kInfoFlagClone)
object->markAsFreed();
@@ -246,12 +246,24 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- uint16 address = scr->validateExportFunc(index);
+ uint16 address = scr->validateExportFunc(index, true);
- // Point to the heap for SCI1.1+ games
- if (getSciVersion() >= SCI_VERSION_1_1)
+ // Point to the heap for SCI1.1 - SCI2.1 games
+ if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
address += scr->getScriptSize();
+ // Bugfix for the intro speed in PQ2 version 1.002.011.
+ // This is taken from the patch by NewRisingSun(NRS) / Belzorash. Global 3
+ // is used for timing during the intro, and in the problematic version it's
+ // initialized to 0, whereas it's 6 in other versions. Thus, we assign it
+ // to 6 here, fixing the speed of the introduction. Refer to bug #3102071.
+ if (g_sci->getGameId() == GID_PQ2 && script == 200) {
+ if (s->variables[VAR_GLOBAL][3].isNull()) {
+ warning("Fixing speed in the intro of PQ2, version 1.002.011");
+ s->variables[VAR_GLOBAL][3] = make_reg(0, 6);
+ }
+ }
+
return make_reg(scriptSeg, address);
}
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 2f00cd7da2..dcabb67e39 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -61,7 +61,7 @@ CREATE_DOSOUND_FORWARD(DoSoundFade)
CREATE_DOSOUND_FORWARD(DoSoundGetPolyphony)
CREATE_DOSOUND_FORWARD(DoSoundUpdateCues)
CREATE_DOSOUND_FORWARD(DoSoundSendMidi)
-CREATE_DOSOUND_FORWARD(DoSoundReverb)
+CREATE_DOSOUND_FORWARD(DoSoundGlobalReverb)
CREATE_DOSOUND_FORWARD(DoSoundSetHold)
CREATE_DOSOUND_FORWARD(DoSoundDummy)
CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability)
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 5ea3178ae5..a66dc8d91f 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -26,6 +26,7 @@
/* String and parser handling */
#include "sci/resource.h"
+#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/message.h"
#include "sci/engine/selector.h"
@@ -616,7 +617,14 @@ reg_t kText(EngineState *s, int argc, reg_t *argv) {
}
reg_t kString(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
+ uint16 op = argv[0].toUint16();
+
+ if (g_sci->_features->detectSci2StringFunctionType() == kSci2StringFunctionNew) {
+ if (op >= 8) // Dup, GetData have been removed
+ op += 2;
+ }
+
+ switch (op) {
case 0: { // New
reg_t stringHandle;
SciString *string = s->_segMan->allocateString(&stringHandle);
@@ -780,6 +788,24 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
Common::String string = s->_segMan->getString(argv[1]);
return make_reg(0, (uint16)atoi(string.c_str()));
}
+ // New subops in SCI2.1 late / SCI3
+ case 14: // unknown
+ warning("kString, subop %d", op);
+ return NULL_REG;
+ case 15: { // upper
+ Common::String string = s->_segMan->getString(argv[1]);
+
+ string.toUppercase();
+ s->_segMan->strcpy(argv[1], string.c_str());
+ return NULL_REG;
+ }
+ case 16: { // lower
+ Common::String string = s->_segMan->getString(argv[1]);
+
+ string.toLowercase();
+ s->_segMan->strcpy(argv[1], string.c_str());
+ return NULL_REG;
+ }
default:
error("Unknown kString subop %d", argv[0].toUint16());
}
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index e97ae38702..9245572bbf 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -44,16 +44,18 @@ void playVideo(Graphics::VideoDecoder *videoDecoder) {
return;
byte *scaleBuffer = 0;
+ byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel;
uint16 width = videoDecoder->getWidth();
uint16 height = videoDecoder->getHeight();
+ uint16 pitch = videoDecoder->getWidth() * bytesPerPixel;
uint16 screenWidth = g_system->getWidth();
uint16 screenHeight = g_system->getHeight();
if (screenWidth == 640 && width <= 320 && height <= 240) {
- assert(videoDecoder->getPixelFormat().bytesPerPixel == 1);
width *= 2;
height *= 2;
- scaleBuffer = new byte[width * height];
+ pitch *= 2;
+ scaleBuffer = new byte[width * height * bytesPerPixel];
}
uint16 x = (screenWidth - width) / 2;
@@ -69,8 +71,8 @@ void playVideo(Graphics::VideoDecoder *videoDecoder) {
if (frame) {
if (scaleBuffer) {
// TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
- g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight());
- g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height);
+ g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
+ g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height);
} else
g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
@@ -165,7 +167,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
break;
}
default:
- warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16());
+ warning("Unhandled SCI kShowMovie subop %d", argv[0].toUint16());
}
}
@@ -287,10 +289,10 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
// Looks to be setting the video size and position. Called with 4 extra integer
// parameters (e.g. 86, 41, 235, 106)
default:
- warningMsg = Common::String::printf("PlayVMD - unsupported subop %d. Params: %d (", operation, argc);
+ warningMsg = Common::String::format("PlayVMD - unsupported subop %d. Params: %d (", operation, argc);
for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += Common::String::format("%04x:%04x", PRINT_REG(argv[i]));
warningMsg += (i == argc - 1 ? ")" : ", ");
}
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 6e1b326c4f..046ed4e500 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -218,7 +218,7 @@ int MessageState::nextMessage(reg_t buf) {
return record.talker;
} else {
MessageTuple &t = _cursorStack.top();
- outputString(buf, Common::String::printf("Msg %d: %d %d %d %d not found", _cursorStack.getModule(), t.noun, t.verb, t.cond, t.seq));
+ outputString(buf, Common::String::format("Msg %d: %d %d %d %d not found", _cursorStack.getModule(), t.noun, t.verb, t.cond, t.seq));
return 0;
}
} else {
diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp
new file mode 100644
index 0000000000..12b2e36d31
--- /dev/null
+++ b/engines/sci/engine/object.cpp
@@ -0,0 +1,269 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+
+#include "sci/engine/kernel.h"
+#include "sci/engine/object.h"
+#include "sci/engine/seg_manager.h"
+
+namespace Sci {
+
+// This helper function is used by Script::relocateLocal and Object::relocate
+// Duplicate in segment.cpp and script.cpp
+static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
+ int rel = location - block_location;
+
+ if (rel < 0)
+ return false;
+
+ uint idx = rel >> 1;
+
+ if (idx >= block.size())
+ return false;
+
+ if (rel & 1) {
+ error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
+ return false;
+ }
+ block[idx].segment = segment; // Perform relocation
+ if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
+ block[idx].offset += scriptSize;
+
+ return true;
+}
+
+void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
+ byte *data = buf + obj_pos.offset;
+ _baseObj = data;
+ _pos = obj_pos;
+
+ if (getSciVersion() <= SCI_VERSION_1_LATE) {
+ _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter));
+ _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2);
+ _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea));
+ _methodCount = READ_LE_UINT16(_baseMethod - 1);
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
+ _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
+ _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
+ _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6));
+ _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod);
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ initSelectorsSci3(buf);
+ }
+
+ if (initVariables) {
+ if (getSciVersion() <= SCI_VERSION_2_1) {
+ for (uint i = 0; i < _variables.size(); i++)
+ _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
+ } else {
+ _infoSelectorSci3 = make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 10));
+ }
+ }
+}
+
+const Object *Object::getClass(SegManager *segMan) const {
+ return isClass() ? this : segMan->getObject(getSuperClassSelector());
+}
+
+int Object::locateVarSelector(SegManager *segMan, Selector slc) const {
+ const byte *buf = 0;
+ uint varnum = 0;
+
+ if (getSciVersion() <= SCI_VERSION_2_1) {
+ const Object *obj = getClass(segMan);
+ varnum = getSciVersion() <= SCI_VERSION_1_LATE ? getVarCount() : obj->getVariable(1).toUint16();
+ buf = (const byte *)obj->_baseVars;
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ varnum = _variables.size();
+ buf = (const byte *)_baseVars;
+ }
+
+ for (uint i = 0; i < varnum; i++)
+ if (READ_SCI11ENDIAN_UINT16(buf + (i << 1)) == slc) // Found it?
+ return i; // report success
+
+ return -1; // Failed
+}
+
+bool Object::relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize) {
+ return relocateBlock(_variables, getPos().offset, segment, location, scriptSize);
+}
+
+bool Object::relocateSci3(SegmentId segment, int location, int offset, size_t scriptSize) {
+ assert(_propertyOffsetsSci3);
+
+ for (uint i = 0; i < _variables.size(); ++i) {
+ if (location == _propertyOffsetsSci3[i]) {
+ _variables[i].segment = segment;
+ _variables[i].offset += offset;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const {
+ int selectors = getVarCount();
+
+ if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) {
+ error("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])",
+ propertyOffset, propertyOffset >> 1, selectors - 1);
+ return -1;
+ }
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2;
+ return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset);
+ } else {
+ const Object *obj = this;
+ if (!isClass())
+ obj = segMan->getObject(getSuperClassSelector());
+
+ return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset);
+ }
+}
+
+void Object::initSpecies(SegManager *segMan, reg_t addr) {
+ uint16 speciesOffset = getSpeciesSelector().offset;
+
+ if (speciesOffset == 0xffff) // -1
+ setSpeciesSelector(NULL_REG); // no species
+ else
+ setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr));
+}
+
+void Object::initSuperClass(SegManager *segMan, reg_t addr) {
+ uint16 superClassOffset = getSuperClassSelector().offset;
+
+ if (superClassOffset == 0xffff) // -1
+ setSuperClassSelector(NULL_REG); // no superclass
+ else
+ setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr));
+}
+
+bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) {
+ const Object *baseObj = segMan->getObject(getSpeciesSelector());
+
+ if (baseObj) {
+ _variables.resize(baseObj->getVarCount());
+ // Copy base from species class, as we need its selector IDs
+ _baseObj = baseObj->_baseObj;
+ if (doInitSuperClass)
+ initSuperClass(segMan, addr);
+ return true;
+ }
+
+ return false;
+}
+
+const int EXTRA_GROUPS = 3;
+
+void Object::initSelectorsSci3(const byte *buf) {
+ const byte *groupInfo = _baseObj + 16;
+ const byte *selectorBase = groupInfo + EXTRA_GROUPS * 32 * 2;
+ int groups = g_sci->getKernel()->getSelectorNamesSize()/32;
+ int methods, properties;
+
+ if (g_sci->getKernel()->getSelectorNamesSize() % 32)
+ ++groups;
+
+ methods = properties = 0;
+
+ // Selectors are divided into groups of 32, of which the first
+ // two selectors are always reserved (because their storage
+ // space is used by the typeMask).
+ // We don't know beforehand how many methods and properties
+ // there are, so we count them first.
+ for (int groupNr = 0; groupNr < groups; ++groupNr) {
+ byte groupLocation = groupInfo[groupNr];
+ const byte *seeker = selectorBase + groupLocation * 32 * 2;
+
+ if (groupLocation != 0) {
+ // This object actually has selectors belonging to this group
+ int typeMask = READ_SCI11ENDIAN_UINT32(seeker);
+
+ for (int bit = 2; bit < 32; ++bit) {
+ int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2);
+ if (typeMask & (1 << bit)) { // Property
+ ++properties;
+ } else if (value != 0xffff) { // Method
+ ++methods;
+ } else {
+ // Undefined selector
+ }
+
+ }
+ }
+ }
+
+ _variables.resize(properties);
+ uint16 *methodIds = (uint16*) malloc(sizeof(uint16)*2*methods);
+ uint16 *propertyIds = (uint16*) malloc(sizeof(uint16)*properties);
+ uint16 *methodOffsets = (uint16*) malloc(sizeof(uint16)*2*methods);
+ uint16 *propertyOffsets = (uint16*) malloc(sizeof(uint16)*properties);
+ int propertyCounter = 0;
+ int methodCounter = 0;
+
+ // Go through the whole thing again to get the property values
+ // and method pointers
+ for (int groupNr = 0; groupNr < groups; ++groupNr) {
+ byte groupLocation = groupInfo[groupNr];
+ const byte *seeker = selectorBase + groupLocation * 32 * 2;
+
+ if (groupLocation != 0) {
+ // This object actually has selectors belonging to this group
+ int typeMask = READ_SCI11ENDIAN_UINT32(seeker);
+ int groupBaseId = groupNr * 32;
+
+ for (int bit = 2; bit < 32; ++bit) {
+ int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2);
+ if (typeMask & (1 << bit)) { // Property
+ propertyIds[propertyCounter] = groupBaseId + bit;
+ _variables[propertyCounter] = make_reg(0, value);
+ propertyOffsets[propertyCounter] = (seeker + bit * 2) - buf;
+ ++propertyCounter;
+ } else if (value != 0xffff) { // Method
+ methodIds[methodCounter * 2] = groupBaseId + bit;
+ methodIds[methodCounter * 2 + 1] = value + READ_SCI11ENDIAN_UINT32(buf);
+ methodOffsets[methodCounter] = (seeker + bit * 2) - buf;
+ ++methodCounter;
+ } else /* Undefined selector */ {};
+
+ }
+ }
+ }
+
+ _speciesSelectorSci3 = make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 4));
+ _superClassPosSci3 = make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 8));
+
+ _baseVars = propertyIds;
+ _baseMethod = methodIds;
+ _methodCount = methods;
+ _propertyOffsetsSci3 = propertyOffsets;
+ //_methodOffsetsSci3 = methodOffsets;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h
new file mode 100644
index 0000000000..842e600b89
--- /dev/null
+++ b/engines/sci/engine/object.h
@@ -0,0 +1,250 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_ENGINE_OBJECT_H
+#define SCI_ENGINE_OBJECT_H
+
+#include "common/array.h"
+#include "common/serializer.h"
+
+#include "sci/sci.h" // for the SCI versions
+#include "sci/engine/vm_types.h" // for reg_t
+#include "sci/util.h"
+
+namespace Sci {
+
+class SegManager;
+
+/** Clone has been marked as 'freed' */
+enum {
+ OBJECT_FLAG_FREED = (1 << 0)
+};
+
+enum infoSelectorFlags {
+ kInfoFlagClone = 0x0001,
+ kInfoFlagClass = 0x8000
+};
+
+enum ObjectOffsets {
+ kOffsetLocalVariables = -6,
+ kOffsetFunctionArea = -4,
+ kOffsetSelectorCounter = -2,
+ kOffsetSelectorSegment = 0,
+ kOffsetInfoSelectorSci0 = 4,
+ kOffsetNamePointerSci0 = 6,
+ kOffsetInfoSelectorSci11 = 14,
+ kOffsetNamePointerSci11 = 16
+};
+
+class Object {
+public:
+ Object() {
+ _offset = getSciVersion() < SCI_VERSION_1_1 ? 0 : 5;
+ _flags = 0;
+ _baseObj = 0;
+ _baseVars = 0;
+ _baseMethod = 0;
+ _methodCount = 0;
+ _propertyOffsetsSci3 = 0;
+ }
+
+ ~Object() { }
+
+ reg_t getSpeciesSelector() const {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ return _variables[_offset];
+ else // SCI3
+ return _speciesSelectorSci3;
+ }
+
+ void setSpeciesSelector(reg_t value) {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ _variables[_offset] = value;
+ else // SCI3
+ _speciesSelectorSci3 = value;
+ }
+
+ reg_t getSuperClassSelector() const {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ return _variables[_offset + 1];
+ else // SCI3
+ return _superClassPosSci3;
+ }
+
+ void setSuperClassSelector(reg_t value) {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ _variables[_offset + 1] = value;
+ else // SCI3
+ _superClassPosSci3 = value;
+ }
+
+ reg_t getInfoSelector() const {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ return _variables[_offset + 2];
+ else // SCI3
+ return _infoSelectorSci3;
+ }
+
+ void setInfoSelector(reg_t info) {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ _variables[_offset + 2] = info;
+ else // SCI3
+ _infoSelectorSci3 = info;
+ }
+
+ // No setter for the -info- selector
+
+ reg_t getNameSelector() const {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ return _offset + 3 < (uint16)_variables.size() ? _variables[_offset + 3] : NULL_REG;
+ else // SCI3
+ return _variables.size() ? _variables[0] : NULL_REG;
+ }
+
+ // No setter for the name selector
+
+ reg_t getPropDictSelector() const {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ return _variables[2];
+ else
+ // This should never occur, this is called from a SCI1.1 - SCI2.1 only function
+ error("getPropDictSelector called for SCI3");
+ }
+
+ void setPropDictSelector(reg_t value) {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ _variables[2] = value;
+ else
+ // This should never occur, this is called from a SCI1.1 - SCI2.1 only function
+ error("setPropDictSelector called for SCI3");
+ }
+
+ reg_t getClassScriptSelector() const {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ return _variables[4];
+ else // SCI3
+ return make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 6));
+ }
+
+ void setClassScriptSelector(reg_t value) {
+ if (getSciVersion() <= SCI_VERSION_2_1)
+ _variables[4] = value;
+ else // SCI3
+ // This should never occur, this is called from a SCI1.1 - SCI2.1 only function
+ error("setClassScriptSelector called for SCI3");
+ }
+
+ Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
+
+ reg_t getFunction(uint16 i) const {
+ uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2;
+ if (getSciVersion() == SCI_VERSION_3)
+ offset--;
+ return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset));
+ }
+
+ Selector getFuncSelector(uint16 i) const {
+ uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1;
+ if (getSciVersion() == SCI_VERSION_3)
+ offset--;
+ return READ_SCI11ENDIAN_UINT16(_baseMethod + offset);
+ }
+
+ /**
+ * Determines if this object is a class and explicitly defines the
+ * selector as a funcselector. Does NOT say anything about the object's
+ * superclasses, i.e. failure may be returned even if one of the
+ * superclasses defines the funcselector
+ */
+ int funcSelectorPosition(Selector sel) const {
+ for (uint i = 0; i < _methodCount; i++)
+ if (getFuncSelector(i) == sel)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Determines if the object explicitly defines slc as a varselector.
+ * Returns -1 if not found.
+ */
+ int locateVarSelector(SegManager *segMan, Selector slc) const;
+
+ bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); }
+ const Object *getClass(SegManager *segMan) const;
+
+ void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
+ bool isFreed() const { return _flags & OBJECT_FLAG_FREED; }
+
+ uint getVarCount() const { return _variables.size(); }
+
+ void init(byte *buf, reg_t obj_pos, bool initVariables = true);
+
+ reg_t getVariable(uint var) const { return _variables[var]; }
+ reg_t &getVariableRef(uint var) { return _variables[var]; }
+
+ uint16 getMethodCount() const { return _methodCount; }
+ reg_t getPos() const { return _pos; }
+
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+ void cloneFromObject(const Object *obj) {
+ _baseObj = obj ? obj->_baseObj : NULL;
+ _baseMethod = obj ? obj->_baseMethod : NULL;
+ _baseVars = obj ? obj->_baseVars : NULL;
+ }
+
+ bool relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize);
+ bool relocateSci3(SegmentId segment, int location, int offset, size_t scriptSize);
+
+ int propertyOffsetToId(SegManager *segMan, int propertyOffset) const;
+
+ void initSpecies(SegManager *segMan, reg_t addr);
+ void initSuperClass(SegManager *segMan, reg_t addr);
+ bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
+ void syncBaseObject(const byte *ptr) { _baseObj = ptr; }
+
+private:
+ void initSelectorsSci3(const byte *buf);
+
+ const byte *_baseObj; /**< base + object offset within base */
+ const uint16 *_baseVars; /**< Pointer to the varselector area for this object */
+ const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */
+ const uint16 *_propertyOffsetsSci3;
+
+ Common::Array<reg_t> _variables;
+ uint16 _methodCount;
+ int _flags;
+ uint16 _offset;
+ reg_t _pos; /**< Object offset within its script; for clones, this is their base */
+ 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 */
+};
+
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_OBJECT_H
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 87e328592f..715b3b5127 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -40,6 +40,7 @@
#include "sci/engine/selector.h"
#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/palette.h"
#include "sci/graphics/ports.h"
#include "sci/sound/audio.h"
@@ -155,9 +156,26 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// Let the object sync custom data
mobj->saveLoadWithSerializer(s);
- // If we are loading a script, hook it up in the script->segment map.
- if (s.isLoading() && type == SEG_TYPE_SCRIPT)
- _scriptSegMap[((Script *)mobj)->getScriptNumber()] = i;
+ // If we are saving a script, save its string heap space too
+ if (s.isSaving() && type == SEG_TYPE_SCRIPT)
+ ((Script *)mobj)->syncStringHeap(s);
+
+ // If we are loading a script, perform some extra steps
+ if (s.isLoading() && type == SEG_TYPE_SCRIPT) {
+ Script *scr = (Script *)mobj;
+ // Hook the script up in the script->segment map
+ _scriptSegMap[scr->getScriptNumber()] = i;
+
+ // Now, load the script itself
+ scr->load(g_sci->getResMan());
+
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
+ it->_value.syncBaseObject(scr->getBuf(it->_value.getPos().offset));
+
+ // Load the script's string heap
+ if (s.getVersion() >= 28)
+ scr->syncStringHeap(s);
+ }
}
s.syncAsSint32LE(_clonesSegId);
@@ -165,6 +183,29 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nodesSegId);
syncArray<Class>(s, _classTable);
+
+ // Now that all scripts are loaded, init their objects
+ for (uint i = 0; i < _heap.size(); i++) {
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
+ continue;
+
+ Script *scr = (Script *)_heap[i];
+ scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
+
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ reg_t addr = it->_value.getPos();
+ Object *obj = scr->scriptObjInit(addr, false);
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ if (!obj->initBaseObject(this, addr, false)) {
+ // TODO/FIXME: This should not be happening at all. It might indicate a possible issue
+ // with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
+ warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ scr->_objects.erase(addr.toUint16());
+ }
+ }
+ }
+ }
}
@@ -175,27 +216,34 @@ void syncWithSerializer(Common::Serializer &s, Class &obj) {
}
static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) {
- // TODO: It would be a good idea to store a magic number & a header size here,
- // so that we can implement backward compatibility if the savegame format changes.
-
- s.syncString(obj.savegame_name);
+ s.syncString(obj.name);
s.syncVersion(CURRENT_SAVEGAME_VERSION);
- obj.savegame_version = s.getVersion();
- s.syncString(obj.game_version);
- s.syncAsSint32LE(obj.savegame_date);
- s.syncAsSint32LE(obj.savegame_time);
+ obj.version = s.getVersion();
+ s.syncString(obj.gameVersion);
+ s.syncAsSint32LE(obj.saveDate);
+ s.syncAsSint32LE(obj.saveTime);
if (s.getVersion() < 22) {
- obj.game_object_offset = 0;
- obj.script0_size = 0;
+ obj.gameObjectOffset = 0;
+ obj.script0Size = 0;
} else {
- s.syncAsUint16LE(obj.game_object_offset);
- s.syncAsUint16LE(obj.script0_size);
+ s.syncAsUint16LE(obj.gameObjectOffset);
+ s.syncAsUint16LE(obj.script0Size);
+ }
+
+ // Playtime
+ obj.playTime = 0;
+ if (s.isLoading()) {
+ if (s.getVersion() >= 26)
+ s.syncAsUint32LE(obj.playTime);
+ } else {
+ obj.playTime = g_engine->getTotalPlayTime() / 1000;
+ s.syncAsUint32LE(obj.playTime);
}
}
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
Common::String tmp;
- s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be game_version
+ s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be gameVersion
if (getSciVersion() <= SCI_VERSION_1_1) {
// Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
@@ -352,6 +400,46 @@ void HunkTable::saveLoadWithSerializer(Common::Serializer &s) {
// Do nothing, hunk tables are not actually saved nor loaded.
}
+void Script::syncStringHeap(Common::Serializer &s) {
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ // Sync all of the SCI_OBJ_STRINGS blocks
+ byte *buf = _buf;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+
+ if (oldScriptHeader)
+ buf += 2;
+
+ do {
+ int blockType = READ_LE_UINT16(buf);
+ int blockSize;
+ if (blockType == 0)
+ break;
+
+ blockSize = READ_LE_UINT16(buf + 2);
+ assert(blockSize > 0);
+
+ if (blockType == SCI_OBJ_STRINGS)
+ s.syncBytes(buf, blockSize);
+
+ buf += blockSize;
+
+ if (_buf - buf == 0)
+ break;
+ } while (1);
+
+ } else {
+ // Strings in SCI1.1 come after the object instances
+ byte *buf = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+
+ // Skip all of the objects
+ while (READ_SCI11ENDIAN_UINT16(buf) == SCRIPT_OBJECT_MAGIC_NUMBER)
+ buf += READ_SCI11ENDIAN_UINT16(buf + 2) * 2;
+
+ // Now, sync everything till the end of the buffer
+ s.syncBytes(buf, _heapSize - (buf - _heapStart));
+ }
+}
+
void Script::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nr);
@@ -469,7 +557,7 @@ void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
soundSetSoundOn(_soundOn);
soundSetMasterVolume(masterVolume);
- setReverb(reverb);
+ setGlobalReverb(reverb);
}
if (s.isSaving())
@@ -513,6 +601,7 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
soundRes = 0;
pMidiParser = 0;
pStreamAud = 0;
+ reverb = -1; // invalid reverb, will be initialized in processInitSound()
}
}
@@ -520,7 +609,7 @@ void SoundCommandParser::syncPlayList(Common::Serializer &s) {
_music->saveLoadWithSerializer(s);
}
-void SoundCommandParser::reconstructPlayList(int savegame_version) {
+void SoundCommandParser::reconstructPlayList() {
Common::StackLock lock(_music->_mutex);
const MusicList::iterator end = _music->getPlayListEnd();
@@ -532,6 +621,14 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) {
(*i)->soundRes = 0;
}
if ((*i)->status == kSoundPlaying) {
+ // Sync the sound object's selectors related to playing with the stored
+ // ones in the playlist, as they may have been invalidated when loading.
+ // Refer to bug #3104624.
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(loop), (*i)->loop);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(priority), (*i)->priority);
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol), (*i)->volume);
+
processPlaySound((*i)->soundObj);
}
}
@@ -595,50 +692,70 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
}
}
-void SegManager::reconstructStack(EngineState *s) {
- DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
- s->stack_base = stack->_entries;
- s->stack_top = s->stack_base + stack->_capacity;
-}
-
-// TODO: Move this function to a more appropriate place, such as vm.cpp or script.cpp
-void SegManager::reconstructScripts(EngineState *s) {
- uint i;
-
- for (i = 0; i < _heap.size(); i++) {
- if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
- continue;
-
- Script *scr = (Script *)_heap[i];
- scr->load(g_sci->getResMan());
- scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
-
- for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
- it->_value._baseObj = scr->getBuf(it->_value.getPos().offset);
- }
-
- for (i = 0; i < _heap.size(); i++) {
- if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
- continue;
-
- Script *scr = (Script *)_heap[i];
-
- for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
- reg_t addr = it->_value.getPos();
- Object *obj = scr->scriptObjInit(addr, false);
-
- if (getSciVersion() < SCI_VERSION_1_1) {
- if (!obj->initBaseObject(this, addr, false)) {
- // TODO/FIXME: This should not be happening at all. It might indicate a possible issue
- // with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
- warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
- scr->scriptObjRemove(addr);
+void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.isLoading())
+ reset(); // remove all script generated windows
+
+ if (s.getVersion() >= 27) {
+ uint windowCount = 0;
+ uint id = PORTS_FIRSTSCRIPTWINDOWID;
+ if (s.isSaving()) {
+ while (id < _windowsById.size()) {
+ if (_windowsById[id])
+ windowCount++;
+ id++;
+ }
+ }
+ // Save/Restore window count
+ s.syncAsUint32LE(windowCount);
+
+ if (s.isSaving()) {
+ id = PORTS_FIRSTSCRIPTWINDOWID;
+ while (id < _windowsById.size()) {
+ if (_windowsById[id]) {
+ Window *window = (Window *)_windowsById[id];
+ window->saveLoadWithSerializer(s);
+ }
+ id++;
+ }
+ } else {
+ id = PORTS_FIRSTSCRIPTWINDOWID;
+ while (windowCount) {
+ Window *window = new Window(0);
+ window->saveLoadWithSerializer(s);
+
+ // add enough entries inside _windowsById as needed
+ while (id <= window->id) {
+ _windowsById.push_back(0);
+ id++;
}
+ _windowsById[window->id] = window;
+ // _windowList may not be 100% correct using that way of restoring
+ // saving/restoring ports won't work perfectly anyway, because the contents
+ // of the window can only get repainted by the scripts and they dont do that
+ // so we will get empty, transparent windows instead. So perfect window order
+ // shouldn't really matter
+ if (window->counterTillFree) {
+ _freeCounter++;
+ } else {
+ if (window->wndStyle & SCI_WINDOWMGR_STYLE_TOPMOST)
+ _windowList.push_front(window);
+ else
+ _windowList.push_back(window);
+ }
+
+ windowCount--;
}
}
}
}
+void SegManager::reconstructStack(EngineState *s) {
+ DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
+ s->stack_base = stack->_entries;
+ s->stack_top = s->stack_base + stack->_capacity;
+}
+
void SegManager::reconstructClones() {
for (uint i = 0; i < _heap.size(); i++) {
SegmentObj *mobj = _heap[i];
@@ -681,15 +798,15 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
g_system->getTimeAndDate(curTime);
SavegameMetadata meta;
- meta.savegame_version = CURRENT_SAVEGAME_VERSION;
- meta.savegame_name = savename;
- meta.game_version = version;
- meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
- meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+ meta.version = CURRENT_SAVEGAME_VERSION;
+ meta.name = savename;
+ meta.gameVersion = version;
+ meta.saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta.saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- meta.script0_size = script0->size;
- meta.game_object_offset = g_sci->getGameObject().offset;
+ meta.script0Size = script0->size;
+ meta.gameObjectOffset = g_sci->getGameObject().offset;
// Checking here again
if (s->executionStackBase) {
@@ -701,6 +818,8 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
sync_SavegameMetadata(ser, meta);
Graphics::saveThumbnail(*fh);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
+ if (g_sci->_gfxPorts)
+ g_sci->_gfxPorts->saveLoadWithSerializer(ser);
return true;
}
@@ -718,13 +837,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if ((meta.savegame_version < MINIMUM_SAVEGAME_VERSION) ||
- (meta.savegame_version > CURRENT_SAVEGAME_VERSION)) {
+ if ((meta.version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta.version > CURRENT_SAVEGAME_VERSION)) {
/*
- if (meta.savegame_version < MINIMUM_SAVEGAME_VERSION)
+ if (meta.version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected, unable to load it");
else
- warning("Savegame version is %d, maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d, maximum supported is %0d", meta.version, CURRENT_SAVEGAME_VERSION);
*/
showScummVMDialog("The format of this saved game is obsolete, unable to load it");
@@ -733,9 +852,9 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if (meta.game_object_offset > 0 && meta.script0_size > 0) {
+ if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- if (script0->size != meta.script0_size || g_sci->getGameObject().offset != meta.game_object_offset) {
+ if (script0->size != meta.script0Size || g_sci->getGameObject().offset != meta.gameObjectOffset) {
//warning("This saved game was created with a different version of the game, unable to load it");
showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
@@ -751,23 +870,22 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
s->reset(true);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
+
// Now copy all current state information
s->_segMan->reconstructStack(s);
- s->_segMan->reconstructScripts(s);
s->_segMan->reconstructClones();
s->initGlobals();
s->gcCountDown = GC_INTERVAL - 1;
// Time state:
s->lastWaitTime = g_system->getMillis();
- s->gameStartTime = g_system->getMillis();
s->_screenUpdateTime = g_system->getMillis();
+ g_engine->setTotalPlayTime(meta.playTime * 1000);
if (g_sci->_gfxPorts)
- g_sci->_gfxPorts->reset();
-
- g_sci->_soundCmd->reconstructPlayList(meta.savegame_version);
+ g_sci->_gfxPorts->saveLoadWithSerializer(ser);
+ g_sci->_soundCmd->reconstructPlayList();
// Message state:
delete s->_msgState;
@@ -789,12 +907,12 @@ bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata
if (stream->eos())
return false;
- if ((meta->savegame_version < MINIMUM_SAVEGAME_VERSION) ||
- (meta->savegame_version > CURRENT_SAVEGAME_VERSION)) {
- if (meta->savegame_version < MINIMUM_SAVEGAME_VERSION)
+ if ((meta->version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta->version > CURRENT_SAVEGAME_VERSION)) {
+ if (meta->version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected- can't load");
else
- warning("Savegame version is %d- maximum supported is %0d", meta->savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d- maximum supported is %0d", meta->version, CURRENT_SAVEGAME_VERSION);
return false;
}
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 14eec4aafc..65a65f7cd5 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,19 +36,20 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 25,
+ CURRENT_SAVEGAME_VERSION = 28,
MINIMUM_SAVEGAME_VERSION = 14
};
// Savegame metadata
struct SavegameMetadata {
- Common::String savegame_name;
- int savegame_version;
- Common::String game_version;
- int savegame_date;
- int savegame_time;
- uint16 game_object_offset;
- uint16 script0_size;
+ Common::String name;
+ int version;
+ Common::String gameVersion;
+ int saveDate;
+ int saveTime;
+ uint32 playTime;
+ uint16 gameObjectOffset;
+ uint16 script0Size;
};
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index da9ab5106d..789df63e87 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -90,8 +90,8 @@ void Script::init(int script_nr, ResourceManager *resMan) {
if (getSciVersion() == SCI_VERSION_0_EARLY) {
_bufSize += READ_LE_UINT16(script->data) * 2;
- } else if (getSciVersion() >= SCI_VERSION_1_1) {
- // In SCI11, the heap was in a separate space from the script. We append
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
+ // In SCI1.1 - SCI2.1, the heap was in a separate space from the script. We append
// it to the end of the script, and adjust addressing accordingly.
// However, since we address the heap with a 16-bit pointer, the
// combined size of the stack and the heap must be 64KB. So far this has
@@ -113,6 +113,15 @@ void Script::init(int script_nr, ResourceManager *resMan) {
error("Script and heap sizes combined exceed 64K. This means a fundamental "
"design bug was made regarding SCI1.1 and newer games.\n"
"Please report this error to the ScummVM team");
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ // Check for scripts over 64KB. These won't work with the current 16-bit address
+ // scheme. We need an overlaying mechanism, or a mechanism to split script parts
+ // in different segments to handle these. For now, simply stop when such a script
+ // is found.
+ // TODO: Remove this once such a mechanism is in place
+ if (script->size > 65535)
+ error("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't "
+ "be handled at the moment, thus stopping", script_nr, script->size);
}
}
@@ -129,7 +138,7 @@ void Script::load(ResourceManager *resMan) {
// Check scripts for matching signatures and patch those, if found
matchSignatureAndPatch(_nr, _buf, script->size);
- if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
assert(heap != 0);
@@ -144,32 +153,49 @@ void Script::load(ResourceManager *resMan) {
_synonyms = 0;
_numSynonyms = 0;
- if (getSciVersion() >= SCI_VERSION_1_1) {
- if (READ_LE_UINT16(_buf + 1 + 5) > 0) { // does the script have an export table?
- _exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
- _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
- }
- _localsOffset = _scriptSize + 4;
- _localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
- } else {
- _exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS);
+ if (getSciVersion() <= SCI_VERSION_1_LATE) {
+ _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS);
if (_exportTable) {
_numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1);
_exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer)
}
- _synonyms = findBlock(SCI_OBJ_SYNONYMS);
+ _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS);
if (_synonyms) {
_numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4;
_synonyms += 4; // skip header
}
- const byte* localsBlock = findBlock(SCI_OBJ_LOCALVARS);
+ const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS);
if (localsBlock) {
_localsOffset = localsBlock - _buf + 4;
_localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size
}
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
+ if (READ_LE_UINT16(_buf + 1 + 5) > 0) { // does the script have an export table?
+ _exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
+ _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
+ }
+ _localsOffset = _scriptSize + 4;
+ _localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ _localsCount = READ_LE_UINT16(_buf + 12);
+ _exportTable = (const uint16 *)(_buf + 22);
+ _numExports = READ_LE_UINT16(_buf + 20);
+ // SCI3 local variables always start dword-aligned
+ if (_numExports % 2)
+ _localsOffset = 22 + _numExports * 2;
+ else
+ _localsOffset = 24 + _numExports * 2;
}
- if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ // SCI0 early
+ // Old script block. There won't be a localvar block in this case.
+ // Instead, the script starts with a 16 bit int specifying the
+ // number of locals we need; these are then allocated and zeroed.
+ _localsCount = READ_LE_UINT16(_buf);
+ _localsOffset = -_localsCount * 2; // Make sure it's invalid
+ } else {
+ // SCI0 late and newer
// Does the script actually have locals? If not, set the locals offset to 0
if (!_localsCount)
_localsOffset = 0;
@@ -178,15 +204,27 @@ void Script::load(ResourceManager *resMan) {
error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
_localsCount = (_bufSize - _localsOffset) >> 1;
}
- } else {
- // Old script block. There won't be a localvar block in this case.
- // Instead, the script starts with a 16 bit int specifying the
- // number of locals we need; these are then allocated and zeroed.
- _localsCount = READ_LE_UINT16(_buf);
- _localsOffset = -_localsCount * 2; // Make sure it's invalid
}
}
+const byte *Script::getSci3ObjectsPointer() {
+ const byte *ptr = 0;
+
+ // SCI3 local variables always start dword-aligned
+ if (_numExports % 2)
+ ptr = _buf + 22 + _numExports * 2;
+ else
+ ptr = _buf + 24 + _numExports * 2;
+
+ // SCI3 object structures always start dword-aligned
+ if (_localsCount % 2)
+ ptr += 2 + _localsCount * 2;
+ else
+ ptr += _localsCount * 2;
+
+ return ptr;
+}
+
Object *Script::getObject(uint16 offset) {
if (_objects.contains(offset))
return &_objects[offset];
@@ -217,13 +255,6 @@ Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
return obj;
}
-void Script::scriptObjRemove(reg_t obj_pos) {
- if (getSciVersion() < SCI_VERSION_1_1)
- obj_pos.offset += 8;
-
- _objects.erase(obj_pos.toUint16());
-}
-
// This helper function is used by Script::relocateLocal and Object::relocate
// Duplicate in segment.cpp and script.cpp
static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
@@ -242,12 +273,28 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
return false;
}
block[idx].segment = segment; // Perform relocation
- if (getSciVersion() >= SCI_VERSION_1_1)
+ if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
block[idx].offset += scriptSize;
return true;
}
+int Script::relocateOffsetSci3(uint32 offset) {
+ int relocStart = READ_LE_UINT32(_buf + 8);
+ int relocCount = READ_LE_UINT16(_buf + 18);
+ const byte *seeker = _buf + relocStart;
+
+ for (int i = 0; i < relocCount; ++i) {
+ if (READ_SCI11ENDIAN_UINT32(seeker) == offset) {
+ // TODO: Find out what UINT16 at (seeker + 8) means
+ return READ_SCI11ENDIAN_UINT16(_buf + offset) + READ_SCI11ENDIAN_UINT32(seeker + 4);
+ }
+ seeker += 10;
+ }
+
+ return -1;
+}
+
bool Script::relocateLocal(SegmentId segment, int location) {
if (_localsBlock)
return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
@@ -255,12 +302,12 @@ bool Script::relocateLocal(SegmentId segment, int location) {
return false;
}
-void Script::relocate(reg_t block) {
+void Script::relocateSci0Sci21(reg_t block) {
const byte *heap = _buf;
uint16 heapSize = (uint16)_bufSize;
uint16 heapOffset = 0;
- if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
heap = _heapStart;
heapSize = (uint16)_heapSize;
heapOffset = _scriptSize;
@@ -295,7 +342,7 @@ void Script::relocate(reg_t block) {
// object, relocate it.
const ObjMap::iterator end = _objects.end();
for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
- if (it->_value.relocate(block.segment, pos, _scriptSize))
+ if (it->_value.relocateSci0Sci21(block.segment, pos, _scriptSize))
break;
}
@@ -303,6 +350,30 @@ void Script::relocate(reg_t block) {
}
}
+void Script::relocateSci3(reg_t block) {
+ const byte *relocStart = _buf + READ_SCI11ENDIAN_UINT32(_buf + 8);
+ //int count = _bufSize - READ_SCI11ENDIAN_UINT32(_buf + 8);
+
+ ObjMap::iterator it;
+ for (it = _objects.begin(); it != _objects.end(); ++it) {
+ unsigned int ofs = it->_value.getPos().offset;
+ unsigned int size = READ_SCI11ENDIAN_UINT16(_buf + ofs + 2);
+ const byte *seeker = relocStart;
+ while (READ_SCI11ENDIAN_UINT32(seeker) < ofs + size &&
+ seeker < _buf + _bufSize) {
+ while (READ_SCI11ENDIAN_UINT32(seeker) < ofs)
+ seeker += 10;
+
+ // TODO: Find out what UINT16 at (seeker + 8) means
+ it->_value.relocateSci3(block.segment,
+ READ_SCI11ENDIAN_UINT32(seeker),
+ READ_SCI11ENDIAN_UINT32(seeker + 4),
+ _scriptSize);
+ seeker += 10;
+ }
+ }
+}
+
void Script::incrementLockers() {
_lockers++;
}
@@ -320,7 +391,7 @@ void Script::setLockers(int lockers) {
_lockers = lockers;
}
-uint16 Script::validateExportFunc(int pubfunct) {
+uint16 Script::validateExportFunc(int pubfunct, bool relocate) {
bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
if (_numExports <= pubfunct) {
@@ -330,17 +401,25 @@ uint16 Script::validateExportFunc(int pubfunct) {
if (exportsAreWide)
pubfunct *= 2;
- uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
+
+ uint16 offset;
+
+ if (getSciVersion() != SCI_VERSION_3 || !relocate) {
+ offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
+ } else {
+ offset = relocateOffsetSci3(pubfunct * 2 + 22);
+ }
+
VERIFY(offset < _bufSize, "invalid export function pointer");
// Check if the offset found points to a second export table (e.g. script 912
// in Camelot and script 306 in KQ4). Such offsets are usually small (i.e. < 10),
// thus easily distinguished from actual code offsets.
// This only makes sense for SCI0-SCI1, as the export table in SCI1.1+ games
- // is located at a specific address, thus findBlock() won't work.
+ // is located at a specific address, thus findBlockSCI0() won't work.
// Fixes bugs #3039785 and #3037595.
if (offset < 10 && getSciVersion() <= SCI_VERSION_1_LATE) {
- const uint16 *secondExportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS, 0);
+ const uint16 *secondExportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS, 0);
if (secondExportTable) {
secondExportTable += 3; // skip header plus 2 bytes (secondExportTable is a uint16 pointer)
@@ -352,7 +431,7 @@ uint16 Script::validateExportFunc(int pubfunct) {
return offset;
}
-byte *Script::findBlock(int type, int skipBlockIndex) {
+byte *Script::findBlockSCI0(int type, int startBlockIndex) {
byte *buf = _buf;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
int blockIndex = 0;
@@ -361,16 +440,16 @@ byte *Script::findBlock(int type, int skipBlockIndex) {
buf += 2;
do {
- int seekerType = READ_LE_UINT16(buf);
+ int blockType = READ_LE_UINT16(buf);
- if (seekerType == 0)
+ if (blockType == 0)
break;
- if (seekerType == type && blockIndex != skipBlockIndex)
+ if (blockType == type && blockIndex > startBlockIndex)
return buf;
- int seekerSize = READ_LE_UINT16(buf + 2);
- assert(seekerSize > 0);
- buf += seekerSize;
+ int blockSize = READ_LE_UINT16(buf + 2);
+ assert(blockSize > 0);
+ buf += blockSize;
blockIndex++;
} while (1);
@@ -423,12 +502,15 @@ void Script::initialiseLocals(SegManager *segMan) {
void Script::initialiseClasses(SegManager *segMan) {
const byte *seeker = 0;
uint16 mult = 0;
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
+
+ if (getSciVersion() <= SCI_VERSION_1_LATE) {
+ seeker = findBlockSCI0(SCI_OBJ_CLASS);
+ mult = 1;
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
mult = 2;
- } else {
- seeker = findBlock(SCI_OBJ_CLASS);
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ seeker = getSci3ObjectsPointer();
mult = 1;
}
@@ -436,7 +518,7 @@ void Script::initialiseClasses(SegManager *segMan) {
return;
uint16 marker;
- bool isClass;
+ bool isClass = false;
uint16 classpos;
int16 species = 0;
@@ -445,29 +527,37 @@ void Script::initialiseClasses(SegManager *segMan) {
marker = READ_SCI11ENDIAN_UINT16(seeker);
classpos = seeker - _buf;
- if (!marker)
+ if (getSciVersion() <= SCI_VERSION_1_LATE && !marker)
break;
- if (getSciVersion() >= SCI_VERSION_1_1) {
- isClass = (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass); // -info- selector
- species = READ_SCI11ENDIAN_UINT16(seeker + 10);
- } else {
+ if (getSciVersion() >= SCI_VERSION_1_1 && marker != 0x1234)
+ break;
+
+ if (getSciVersion() <= SCI_VERSION_1_LATE) {
isClass = (marker == SCI_OBJ_CLASS);
if (isClass)
species = READ_SCI11ENDIAN_UINT16(seeker + 12);
classpos += 12;
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
+ isClass = (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass); // -info- selector
+ species = READ_SCI11ENDIAN_UINT16(seeker + 10);
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ isClass = (READ_SCI11ENDIAN_UINT16(seeker + 10) & kInfoFlagClass);
+ species = READ_SCI11ENDIAN_UINT16(seeker + 4);
}
if (isClass) {
// WORKAROUNDs for off-by-one script errors
- if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo() && species == (int)segMan->classTableSize())
- segMan->resizeClassTable(segMan->classTableSize() + 1);
- if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500 && species == (int)segMan->classTableSize())
- segMan->resizeClassTable(segMan->classTableSize() + 1);
- if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93 && species == (int)segMan->classTableSize())
- segMan->resizeClassTable(segMan->classTableSize() + 1);
- if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99 && species == (int)segMan->classTableSize())
- segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (species == (int)segMan->classTableSize()) {
+ if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo())
+ segMan->resizeClassTable(species + 1);
+ else if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500)
+ segMan->resizeClassTable(species + 1);
+ else if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93)
+ segMan->resizeClassTable(species + 1);
+ else if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99)
+ segMan->resizeClassTable(species + 1);
+ }
if (species < 0 || species >= (int)segMan->classTableSize())
error("Invalid species %d(0x%x) unknown max %d(0x%x) while instantiating script %d\n",
@@ -483,44 +573,45 @@ void Script::initialiseClasses(SegManager *segMan) {
void Script::initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId) {
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- const byte *seeker = _buf + (oldScriptHeader ? 2 : 0);
-
- do {
- uint16 objType = READ_SCI11ENDIAN_UINT16(seeker);
- if (!objType)
- break;
- switch (objType) {
- case SCI_OBJ_OBJECT:
- case SCI_OBJ_CLASS:
- {
- reg_t addr = make_reg(segmentId, seeker - _buf + 4);
- Object *obj = scriptObjInit(addr);
- obj->initSpecies(segMan, addr);
-
- if (!obj->initBaseObject(segMan, addr)) {
- if (_nr == 202 && g_sci->getGameId() == GID_KQ5) {
- // WORKAROUND: Script 202 of KQ5 French and German
- // (perhaps Spanish too?) has an invalid object.
- // This is non-fatal. Refer to bug #3035396.
- } else {
- error("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ // We need to make two passes, as the objects in the script might be in the
+ // wrong order (e.g. in the demo of Iceman) - refer to bug #3034713
+ for (int pass = 1; pass <= 2; pass++) {
+ const byte *seeker = _buf + (oldScriptHeader ? 2 : 0);
+
+ do {
+ uint16 objType = READ_SCI11ENDIAN_UINT16(seeker);
+ if (!objType)
+ break;
+
+ switch (objType) {
+ case SCI_OBJ_OBJECT:
+ case SCI_OBJ_CLASS:
+ {
+ reg_t addr = make_reg(segmentId, seeker - _buf + 4);
+ Object *obj = scriptObjInit(addr);
+ obj->initSpecies(segMan, addr);
+
+ if (pass == 2) {
+ if (!obj->initBaseObject(segMan, addr)) {
+ error("Failed to locate base object for object at %04X:%04X", PRINT_REG(addr));
+ //scriptObjRemove(addr);
+ }
}
- scriptObjRemove(addr);
}
- }
- break;
+ break;
- default:
- break;
- }
+ default:
+ break;
+ }
- seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
- } while ((uint32)(seeker - _buf) < getScriptSize() - 2);
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
+ } while ((uint32)(seeker - _buf) < getScriptSize() - 2);
+ }
- byte *relocationBlock = findBlock(SCI_OBJ_POINTERS);
+ byte *relocationBlock = findBlockSCI0(SCI_OBJ_POINTERS);
if (relocationBlock)
- relocate(make_reg(segmentId, relocationBlock - getBuf() + 4));
+ relocateSci0Sci21(make_reg(segmentId, relocationBlock - getBuf() + 4));
}
void Script::initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId) {
@@ -555,14 +646,30 @@ void Script::initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId) {
seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
}
- relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(_heapStart)));
+ relocateSci0Sci21(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(_heapStart)));
+}
+
+void Script::initialiseObjectsSci3(SegManager *segMan, SegmentId segmentId) {
+ const byte *seeker = getSci3ObjectsPointer();
+
+ while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
+ reg_t reg = make_reg(segmentId, seeker - _buf);
+ Object *obj = scriptObjInit(reg);
+
+ obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
+ }
+
+ relocateSci3(make_reg(segmentId, 0));
}
void Script::initialiseObjects(SegManager *segMan, SegmentId segmentId) {
- if (getSciVersion() >= SCI_VERSION_1_1)
- initialiseObjectsSci11(segMan, segmentId);
- else
+ if (getSciVersion() <= SCI_VERSION_1_LATE)
initialiseObjectsSci0(segMan, segmentId);
+ else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
+ initialiseObjectsSci11(segMan, segmentId);
+ else if (getSciVersion() == SCI_VERSION_3)
+ initialiseObjectsSci3(segMan, segmentId);
}
reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index e316fc0c8d..18d7157747 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -137,12 +137,6 @@ public:
Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true);
/**
- * Removes a script object
- * @param obj_pos Location (segment, offset) of the object.
- */
- void scriptObjRemove(reg_t obj_pos);
-
- /**
* Initializes the script's local variables
* @param segMan A reference to the segment manager
*/
@@ -206,10 +200,11 @@ public:
* Validate whether the specified public function is exported by
* the script in the specified segment.
* @param pubfunct Index of the function to validate
+ * @param relocate Decide whether to relocate this public function or not
* @return NULL if the public function is invalid, its
* offset into the script's segment otherwise
*/
- uint16 validateExportFunc(int pubfunct);
+ uint16 validateExportFunc(int pubfunct, bool relocate);
/**
* Marks the script as deleted.
@@ -238,23 +233,52 @@ public:
void mcpyInOut(int dst, const void *src, size_t n);
/**
- * Finds the pointer where a block of a specific type starts from
+ * Finds the pointer where a block of a specific type starts from,
+ * in SCI0 - SCI1 games
*/
- byte *findBlock(int type, int skipBlockIndex = -1);
+ byte *findBlockSCI0(int type, int startBlockIndex = -1);
+
+ /**
+ * Syncs the string heap of a script. Used when saving/loading.
+ */
+ void syncStringHeap(Common::Serializer &ser);
+
+ /**
+ * Resolve a relocation in an SCI3 script
+ * @param offset The offset to relocate from
+ */
+ int relocateOffsetSci3(uint32 offset);
+
+ /**
+ * Gets an offset to the beginning of the code block in a SCI3 script
+ */
+ int getCodeBlockOffset() { return READ_SCI11ENDIAN_UINT32(_buf); }
private:
/**
- * Processes a relocation block witin a script
+ * Processes a relocation block within a SCI0-SCI2.1 script
+ * This function is idempotent, but it must only be called after all
+ * objects have been instantiated, or a run-time error will occur.
+ * @param obj_pos Location (segment, offset) of the block
+ */
+ void relocateSci0Sci21(reg_t block);
+
+ /**
+ * Processes a relocation block within a SCI3 script
* This function is idempotent, but it must only be called after all
* objects have been instantiated, or a run-time error will occur.
* @param obj_pos Location (segment, offset) of the block
- * @return Location of the relocation block
*/
- void relocate(reg_t block);
+ void relocateSci3(reg_t block);
bool relocateLocal(SegmentId segment, int location);
/**
+ * Gets a pointer to the beginning of the objects in a SCI3 script
+ */
+ const byte *getSci3ObjectsPointer();
+
+ /**
* Initializes the script's objects (SCI0)
* @param segMan A reference to the segment manager
* @param segmentId The script's segment id
@@ -262,11 +286,18 @@ private:
void initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId);
/**
- * Initializes the script's objects (SCI1.1+)
+ * Initializes the script's objects (SCI1.1 - SCI2.1)
* @param segMan A reference to the segment manager
* @param segmentId The script's segment id
*/
void initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId);
+
+ /**
+ * Initializes the script's objects (SCI3)
+ * @param segMan A reference to the segment manager
+ * @param segmentId The script's segment id
+ */
+ void initialiseObjectsSci3(SegManager *segMan, SegmentId segmentId);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 42d7c4d9cd..84e8c4759e 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -60,6 +60,8 @@ struct SciScriptSignature {
// - if not EOS, an adjust offset and the actual bytes
// - rinse and repeat
+#if 0
+
// ===========================================================================
// Castle of Dr. Brain
// cipher::init (script 391) is called on room 380 init. This resets the word
@@ -101,10 +103,12 @@ const uint16 castlebrainPatchCipherPuzzle[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature castlebrainSignatures[] = {
- { 391, "cipher puzzle save/restore break", 1, PATCH_MAGICDWORD(0xa3, 0x26, 0xa3, 0x25), -2, castlebrainSignatureCipherPuzzle, castlebrainPatchCipherPuzzle },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 391, "cipher puzzle save/restore break", 1, PATCH_MAGICDWORD(0xa3, 0x26, 0xa3, 0x25), -2, castlebrainSignatureCipherPuzzle, castlebrainPatchCipherPuzzle },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
+#endif
+
// ===========================================================================
// stayAndHelp::changeState (0) is called when ego swims to the left or right
// boundaries of room 660. Normally a textbox is supposed to get on screen
@@ -167,8 +171,8 @@ const uint16 ecoquest1PatchStayAndHelp[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature ecoquest1Signatures[] = {
- { 660, "CD: bad messagebox and freeze", 1, PATCH_MAGICDWORD(0x38, 0x22, 0x01, 0x78), -17, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 660, "CD: bad messagebox and freeze", 1, PATCH_MAGICDWORD(0x38, 0x22, 0x01, 0x78), -17, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -229,8 +233,8 @@ const uint16 ecoquest2PatchEcorder[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature ecoquest2Signatures[] = {
- { 50, "initial text not removed on ecorder", 1, PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -8, ecoquest2SignatureEcorder, ecoquest2PatchEcorder },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 50, "initial text not removed on ecorder", 1, PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -8, ecoquest2SignatureEcorder, ecoquest2PatchEcorder },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -322,10 +326,10 @@ const uint16 freddypharkasPatchLadderEvent[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature freddypharkasSignatures[] = {
- { 0, "CD: score early disposal", 1, PATCH_MAGICDWORD(0x39, 0x0d, 0x43, 0x75), -3, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
- { 235, "CD: cannister pickup hang", 3, PATCH_MAGICDWORD(0x39, 0x07, 0x39, 0x08), -4, freddypharkasSignatureCannisterHang, freddypharkasPatchCannisterHang },
- { 320, "ladder event issue", 2, PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 0, "CD: score early disposal", 1, PATCH_MAGICDWORD(0x39, 0x0d, 0x43, 0x75), -3, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
+ { 235, "CD: cannister pickup hang", 3, PATCH_MAGICDWORD(0x39, 0x07, 0x39, 0x08), -4, freddypharkasSignatureCannisterHang, freddypharkasPatchCannisterHang },
+ { 320, "ladder event issue", 2, PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -396,75 +400,79 @@ const uint16 gk1PatchDay5PhoneFreeze[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature gk1Signatures[] = {
- { 212, "day 5 phone freeze", 1, PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
- { 230, "day 6 police beignet timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
- { 230, "day 6 police sleep timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 212, "day 5 phone freeze", 1, PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
+ { 230, "day 6 police beignet timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
+ { 230, "day 6 police sleep timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
+#if 0
+
// ===========================================================================
// this here gets called on entry and when going out of game windows
// uEvt::port will not get changed after kDisposeWindow but a bit later, so
// we would get an invalid port handle to a kSetPort call. We just patch in
// resetting of the port selector. We destroy the stop/fade code in there,
// it seems it isn't used at all in the game.
-//const byte hoyle4SignaturePortFix[] = {
-// 28,
-// 0x39, 0x09, // pushi 09
-// 0x89, 0x0b, // lsg 0b
-// 0x39, 0x64, // pushi 64
-// 0x38, 0xc8, 0x00, // pushi 00c8
-// 0x38, 0x2c, 0x01, // pushi 012c
-// 0x38, 0x90, 0x01, // pushi 0190
-// 0x38, 0xf4, 0x01, // pushi 01f4
-// 0x38, 0x58, 0x02, // pushi 0258
-// 0x38, 0xbc, 0x02, // pushi 02bc
-// 0x38, 0x20, 0x03, // pushi 0320
-// 0x46, // calle [xxxx] [xxxx] [xx]
-// +5, 43, // [skip 5 bytes]
-// 0x30, 0x27, 0x00, // bnt 0027 -> end of routine
-// 0x87, 0x00, // lap 00
-// 0x30, 0x19, 0x00, // bnt 0019 -> fade out
-// 0x87, 0x01, // lap 01
-// 0x30, 0x14, 0x00, // bnt 0014 -> fade out
-// 0x38, 0xa7, 0x00, // pushi 00a7
-// 0x76, // push0
-// 0x80, 0x29, 0x01, // lag 0129
-// 0x4a, 0x04, // send 04 - call song::stop
-// 0x39, 0x27, // pushi 27
-// 0x78, // push1
-// 0x8f, 0x01, // lsp 01
-// 0x51, 0x54, // class 54
-// 0x4a, 0x06, // send 06 - call PlaySong::play
-// 0x33, 0x09, // jmp 09 -> end of routine
-// 0x38, 0xaa, 0x00, // pushi 00aa
-// 0x76, // push0
-// 0x80, 0x29, 0x01, // lag 0129
-// 0x4a, 0x04, // send 04
-// 0x48, // ret
-// 0
-//};
-
-//const uint16 hoyle4PatchPortFix[] = {
-// PATCH_ADDTOOFFSET | +33,
-// 0x38, 0x31, 0x01, // pushi 0131 (selector curEvent)
-// 0x76, // push0
-// 0x80, 0x50, 0x00, // lag 0050 (global var 80h, "User")
-// 0x4a, 0x04, // send 04 - read User::curEvent
-//
-// 0x38, 0x93, 0x00, // pushi 0093 (selector port)
-// 0x78, // push1
-// 0x76, // push0
-// 0x4a, 0x06, // send 06 - write 0 to that object::port
-// 0x48, // ret
-// PATCH_END
-//};
+const byte hoyle4SignaturePortFix[] = {
+ 28,
+ 0x39, 0x09, // pushi 09
+ 0x89, 0x0b, // lsg 0b
+ 0x39, 0x64, // pushi 64
+ 0x38, 0xc8, 0x00, // pushi 00c8
+ 0x38, 0x2c, 0x01, // pushi 012c
+ 0x38, 0x90, 0x01, // pushi 0190
+ 0x38, 0xf4, 0x01, // pushi 01f4
+ 0x38, 0x58, 0x02, // pushi 0258
+ 0x38, 0xbc, 0x02, // pushi 02bc
+ 0x38, 0x20, 0x03, // pushi 0320
+ 0x46, // calle [xxxx] [xxxx] [xx]
+ +5, 43, // [skip 5 bytes]
+ 0x30, 0x27, 0x00, // bnt 0027 -> end of routine
+ 0x87, 0x00, // lap 00
+ 0x30, 0x19, 0x00, // bnt 0019 -> fade out
+ 0x87, 0x01, // lap 01
+ 0x30, 0x14, 0x00, // bnt 0014 -> fade out
+ 0x38, 0xa7, 0x00, // pushi 00a7
+ 0x76, // push0
+ 0x80, 0x29, 0x01, // lag 0129
+ 0x4a, 0x04, // send 04 - call song::stop
+ 0x39, 0x27, // pushi 27
+ 0x78, // push1
+ 0x8f, 0x01, // lsp 01
+ 0x51, 0x54, // class 54
+ 0x4a, 0x06, // send 06 - call PlaySong::play
+ 0x33, 0x09, // jmp 09 -> end of routine
+ 0x38, 0xaa, 0x00, // pushi 00aa
+ 0x76, // push0
+ 0x80, 0x29, 0x01, // lag 0129
+ 0x4a, 0x04, // send 04
+ 0x48, // ret
+ 0
+};
+
+const uint16 hoyle4PatchPortFix[] = {
+ PATCH_ADDTOOFFSET | +33,
+ 0x38, 0x31, 0x01, // pushi 0131 (selector curEvent)
+ 0x76, // push0
+ 0x80, 0x50, 0x00, // lag 0050 (global var 80h, "User")
+ 0x4a, 0x04, // send 04 - read User::curEvent
+
+ 0x38, 0x93, 0x00, // pushi 0093 (selector port)
+ 0x78, // push1
+ 0x76, // push0
+ 0x4a, 0x06, // send 06 - write 0 to that object::port
+ 0x48, // ret
+ PATCH_END
+};
// script, description, magic DWORD, adjust
-//const SciScriptSignature hoyle4Signatures[] = {
-// { 0, "port fix when disposing windows", PATCH_MAGICDWORD(0x64, 0x38, 0xC8, 0x00), -5, hoyle4SignaturePortFix, hoyle4PatchPortFix },
-// { 0, NULL, 0, 0, NULL, NULL }
-//};
+const SciScriptSignature hoyle4Signatures[] = {
+ { 0, "port fix when disposing windows", PATCH_MAGICDWORD(0x64, 0x38, 0xC8, 0x00), -5, hoyle4SignaturePortFix, hoyle4PatchPortFix },
+ { 0, NULL, 0, 0, NULL, NULL }
+};
+
+#endif
// ===========================================================================
// at least during harpy scene export 29 of script 0 is called in kq5cd and
@@ -518,8 +526,8 @@ const uint16 kq5PatchCdHarpyVolume[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature kq5Signatures[] = {
- { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -569,8 +577,8 @@ const uint16 larry6PatchDeathDialog[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature larry6Signatures[] = {
- { 82, "death dialog memory corruption", 1, PATCH_MAGICDWORD(0x3e, 0x33, 0x01, 0x35), 0, larry6SignatureDeathDialog, larry6PatchDeathDialog },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 82, "death dialog memory corruption", 1, PATCH_MAGICDWORD(0x3e, 0x33, 0x01, 0x35), 0, larry6SignatureDeathDialog, larry6PatchDeathDialog },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -600,8 +608,8 @@ const uint16 laurabow2PatchPaintingClosing[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature laurabow2Signatures[] = {
- { 560, "painting closing immediately", 1, PATCH_MAGICDWORD(0x36, 0x81, 0x0b, 0x1c), -2, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 560, "painting closing immediately", 1, PATCH_MAGICDWORD(0x36, 0x81, 0x0b, 0x1c), -2, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -642,10 +650,10 @@ const uint16 mothergoose256PatchSaveLimit[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature mothergoose256Signatures[] = {
- { 0, "replay save issue", 1, PATCH_MAGICDWORD(0x20, 0x04, 0xa1, 0xb3), -2, mothergoose256SignatureReplay, mothergoose256PatchReplay },
- { 0, "save limit dialog (SCI1.1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
- { 994, "save limit dialog (SCI1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 0, "replay save issue", 1, PATCH_MAGICDWORD(0x20, 0x04, 0xa1, 0xb3), -2, mothergoose256SignatureReplay, mothergoose256PatchReplay },
+ { 0, "save limit dialog (SCI1.1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
+ { 994, "save limit dialog (SCI1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -699,9 +707,75 @@ const uint16 qfg1vgaPatchFightEvents[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature qfg1vgaSignatures[] = {
- { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
+// Script 944 in QFG2 contains the FileSelector system class, used in the
+// character import screen. This gets incorrectly called constantly, whenever
+// the user clicks on a button in order to refresh the file list. This was
+// probably done because it would be easier to refresh the list whenever the
+// user inserted a new floppy disk, or changed directory. The problem is that
+// the script has a bug, and it invalidates the text of the entries in the
+// list. This has a high probability of breaking, as the user could change the
+// list very quickly, or the garbage collector could kick in and remove the
+// deleted entries. We don't allow the user to change the directory, thus the
+// contents of the file list are constant, so we can avoid the constant file
+// and text entry refreshes whenever a button is pressed, and prevent possible
+// crashes because of these constant quick object reallocations. Fixes bug
+// #3037996.
+const byte qfg2SignatureImportDialog[] = {
+ 16,
+ 0x63, 0x20, // pToa text
+ 0x30, 0x0b, 0x00, // bnt [next state]
+ 0x7a, // push2
+ 0x39, 0x03, // pushi 03
+ 0x36, // push
+ 0x43, 0x72, 0x04, // callk Memory 4
+ 0x35, 0x00, // ldi 00
+ 0x65, 0x20, // aTop text
+ 0
+};
+
+const uint16 qfg2PatchImportDialog[] = {
+ PATCH_ADDTOOFFSET | +5,
+ 0x48, // ret
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature qfg2Signatures[] = {
+ { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x20, 0x30, 0x0b, 0x00), -1, qfg2SignatureImportDialog, qfg2PatchImportDialog },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
+// Patch for the import screen in QFG3, same as the one for QFG2 above
+const byte qfg3SignatureImportDialog[] = {
+ 15,
+ 0x63, 0x2a, // pToa text
+ 0x31, 0x0b, // bnt [next state]
+ 0x7a, // push2
+ 0x39, 0x03, // pushi 03
+ 0x36, // push
+ 0x43, 0x72, 0x04, // callk Memory 4
+ 0x35, 0x00, // ldi 00
+ 0x65, 0x2a, // aTop text
+ 0
+};
+
+const uint16 qfg3PatchImportDialog[] = {
+ PATCH_ADDTOOFFSET | +4,
+ 0x48, // ret
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature qfg3Signatures[] = {
+ { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -721,6 +795,17 @@ const byte sq4FloppySignatureEndlessFlight[] = {
0
};
+// Similar to the above, for the German version (ffs. bug #3110215)
+const byte sq4FloppySignatureEndlessFlightGerman[] = {
+ 8,
+ 0x39, 0x04, // pushi 04 (selector x)
+ 0x78, // push1
+ 0x67, 0x08, // pTos 08 (property x)
+ 0x63, 0x4c, // pToa 4c (invalid property)
+ 0x02, // add
+ 0
+};
+
const uint16 sq4FloppyPatchEndlessFlight[] = {
PATCH_ADDTOOFFSET | +5,
0x35, 0x03, // ldi 03 (which would be the content of the property)
@@ -729,8 +814,9 @@ const uint16 sq4FloppyPatchEndlessFlight[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature sq4Signatures[] = {
- { 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
+ { 298, "Floppy (German): endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x4c), -3, sq4FloppySignatureEndlessFlightGerman, sq4FloppyPatchEndlessFlight },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -777,8 +863,8 @@ const uint16 sq5PatchScrubbing[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature sq5Signatures[] = {
- { 119, "scrubbing send crash", 1, PATCH_MAGICDWORD(0x18, 0x31, 0x37, 0x78), 0, sq5SignatureScrubbing, sq5PatchScrubbing },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 119, "scrubbing send crash", 1, PATCH_MAGICDWORD(0x18, 0x31, 0x37, 0x78), 0, sq5SignatureScrubbing, sq5PatchScrubbing },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -874,9 +960,12 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr
void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
const SciScriptSignature *signatureTable = NULL;
switch (g_sci->getGameId()) {
+ // Dr. Brain now works because we properly maintain the state of the string heap in savegames
+#if 0
case GID_CASTLEBRAIN:
signatureTable = castlebrainSignatures;
break;
+#endif
case GID_ECOQUEST:
signatureTable = ecoquest1Signatures;
break;
@@ -890,9 +979,11 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin
signatureTable = gk1Signatures;
break;
// hoyle4 now works due to workaround inside GfxPorts
- //case GID_HOYLE4:
- // signatureTable = hoyle4Signatures;
- // break;
+#if 0
+ case GID_HOYLE4:
+ signatureTable = hoyle4Signatures;
+ break;
+#endif
case GID_KQ5:
signatureTable = kq5Signatures;
break;
@@ -908,6 +999,12 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin
case GID_QFG1VGA:
signatureTable = qfg1vgaSignatures;
break;
+ case GID_QFG2:
+ signatureTable = qfg2Signatures;
+ break;
+ case GID_QFG3:
+ signatureTable = qfg3Signatures;
+ break;
case GID_SQ4:
signatureTable = sq4Signatures;
break;
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 9c08526fbb..f1d08646d1 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -64,7 +64,7 @@ const char *opcodeNames[] = {
};
// Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered.
-reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode) {
+reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode) {
SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT);
Script *script_entity = NULL;
const byte *scr;
@@ -85,7 +85,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if (pos.offset >= scr_size) {
warning("Trying to disassemble beyond end of script");
- return pos;
+ return NULL_REG;
}
int16 opparams[4];
@@ -95,25 +95,25 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
opsize &= 1; // byte if true, word if false
- printf("%04x:%04x: ", PRINT_REG(pos));
+ debugN("%04x:%04x: ", PRINT_REG(pos));
- if (print_bytecode) {
+ if (printBytecode) {
if (pos.offset + bytecount > scr_size) {
warning("Operation arguments extend beyond end of script");
return retval;
}
for (i = 0; i < bytecount; i++)
- printf("%02x ", scr[pos.offset + i]);
+ debugN("%02x ", scr[pos.offset + i]);
for (i = bytecount; i < 5; i++)
- printf(" ");
+ debugN(" ");
}
- if (print_bw_tag)
- printf("[%c] ", opsize ? 'B' : 'W');
+ if (printBWTag)
+ debugN("[%c] ", opsize ? 'B' : 'W');
- printf("%s", opcodeNames[opcode]);
+ debugN("%s", opcodeNames[opcode]);
i = 0;
while (g_opcode_formats[opcode][i]) {
@@ -124,12 +124,15 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
case Script_SByte:
case Script_Byte:
- printf(" %02x", scr[retval.offset++]);
+ param_value = scr[retval.offset];
+ debugN(" %02x", scr[retval.offset]);
+ retval.offset++;
break;
case Script_Word:
case Script_SWord:
- printf(" %04x", READ_SCI11ENDIAN_UINT16(&scr[retval.offset]));
+ param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
+ debugN(" %04x", READ_SCI11ENDIAN_UINT16(&scr[retval.offset]));
retval.offset += 2;
break;
@@ -148,11 +151,11 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
}
if (opcode == op_callk)
- printf(" %s[%x]", (param_value < kernel->_kernelFuncs.size()) ?
+ debugN(" %s[%x]", (param_value < kernel->_kernelFuncs.size()) ?
((param_value < kernel->getKernelNamesSize()) ? kernel->getKernelName(param_value).c_str() : "[Unknown(postulated)]")
: "<invalid>", param_value);
else
- printf(opsize ? " %02x" : " %04x", param_value);
+ debugN(opsize ? " %02x" : " %04x", param_value);
break;
@@ -163,7 +166,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
retval.offset += 2;
}
- printf(opsize ? " %02x" : " %04x", param_value);
+ debugN(opsize ? " %02x" : " %04x", param_value);
break;
case Script_SRelative:
@@ -173,7 +176,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
retval.offset += 2;
}
- printf(opsize ? " %02x [%04x]" : " %04x [%04x]", param_value, (0xffff) & (retval.offset + param_value));
+ debugN(opsize ? " %02x [%04x]" : " %04x [%04x]", param_value, (0xffff) & (retval.offset + param_value));
break;
case Script_End:
@@ -190,14 +193,18 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) ||
(opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) {
const Object *obj = s->_segMan->getObject(s->xs->objp);
- if (!obj)
+ if (!obj) {
warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(s->xs->objp));
- else
- printf(" (%s)", g_sci->getKernel()->getSelectorName(obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1])).c_str());
+ } else {
+ if (getSciVersion() == SCI_VERSION_3)
+ debugN("\t(%s)", g_sci->getKernel()->getSelectorName(param_value).c_str());
+ else
+ debugN("\t(%s)", g_sci->getKernel()->getSelectorName(obj->propertyOffsetToId(s->_segMan, param_value)).c_str());
+ }
}
}
- printf("\n");
+ debugN("\n");
if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if (opcode == op_callk) {
@@ -208,14 +215,14 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if (!oldScriptHeader)
argc += (s->restAdjust);
- printf(" Kernel params: (");
+ debugN(" Kernel params: (");
for (int j = 0; j < argc; j++) {
- printf("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe]));
+ debugN("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe]));
if (j + 1 < argc)
- printf(", ");
+ debugN(", ");
}
- printf(")\n");
+ debugN(")\n");
} else if ((opcode == op_send) || (opcode == op_self)) {
int restmod = s->restAdjust;
int stackframe = (scr[pos.offset + 1] >> 1) + restmod;
@@ -240,32 +247,32 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if (!name)
name = "<invalid>";
- printf(" %s::%s[", name, g_sci->getKernel()->getSelectorName(selector).c_str());
+ debugN(" %s::%s[", name, g_sci->getKernel()->getSelectorName(selector).c_str());
switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) {
case kSelectorMethod:
- printf("FUNCT");
+ debugN("FUNCT");
argc += restmod;
restmod = 0;
break;
case kSelectorVariable:
- printf("VAR");
+ debugN("VAR");
break;
case kSelectorNone:
- printf("INVALID");
+ debugN("INVALID");
break;
}
- printf("](");
+ debugN("](");
while (argc--) {
- printf("%04x:%04x", PRINT_REG(sb[- stackframe + 2]));
+ debugN("%04x:%04x", PRINT_REG(sb[- stackframe + 2]));
if (argc)
- printf(", ");
+ debugN(", ");
stackframe--;
}
- printf(")\n");
+ debugN(")\n");
stackframe -= 2;
} // while (stackframe > 0)
} // Send-like opcodes
@@ -334,8 +341,8 @@ void SciEngine::scriptDebug() {
// OK, found whatever we were looking for
}
- printf("Step #%d\n", s->scriptStepCounter);
- disassemble(s, s->xs->addr.pc, 0, 1);
+ debugN("Step #%d\n", s->scriptStepCounter);
+ disassemble(s, s->xs->addr.pc, false, true);
if (_debugState.runningStep) {
_debugState.runningStep--;
@@ -354,27 +361,27 @@ void Kernel::dumpScriptObject(char *data, int seeker, int objsize) {
int namepos = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 14 + seeker);
int i = 0;
- printf("Object\n");
+ debugN("Object\n");
Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker);
//-4 because the size includes the two-word header
- printf("Name: %s\n", namepos ? ((char *)(data + namepos)) : "<unknown>");
- printf("Superclass: %x\n", superclass);
- printf("Species: %x\n", species);
- printf("-info-:%x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 12 + seeker) & 0xffff);
+ debugN("Name: %s\n", namepos ? ((char *)(data + namepos)) : "<unknown>");
+ debugN("Superclass: %x\n", superclass);
+ debugN("Species: %x\n", species);
+ debugN("-info-:%x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 12 + seeker) & 0xffff);
- printf("Function area offset: %x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + seeker + 4));
- printf("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + seeker + 6)));
+ debugN("Function area offset: %x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + seeker + 4));
+ debugN("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + seeker + 6)));
seeker += 8;
while (selectors--) {
- printf(" [#%03x] = 0x%x\n", i++, (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker) & 0xffff);
+ debugN(" [#%03x] = 0x%x\n", i++, (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker) & 0xffff);
seeker += 2;
}
- printf("Overridden functions: %x\n", selectors = overloads = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker));
+ debugN("Overridden functions: %x\n", selectors = overloads = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker));
seeker += 2;
@@ -382,8 +389,8 @@ void Kernel::dumpScriptObject(char *data, int seeker, int objsize) {
while (overloads--) {
int selector = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + (seeker));
- printf(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>");
- printf("%04x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + selectors*2 + 2) & 0xffff);
+ debugN(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>");
+ debugN("%04x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + selectors*2 + 2) & 0xffff);
seeker += 2;
}
@@ -395,17 +402,17 @@ void Kernel::dumpScriptClass(char *data, int seeker, int objsize) {
int superclass = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 10 + seeker);
int namepos = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 14 + seeker);
- printf("Class\n");
+ debugN("Class\n");
Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker);
- printf("Name: %s\n", namepos ? ((char *)data + namepos) : "<unknown>");
- printf("Superclass: %x\n", superclass);
- printf("Species: %x\n", species);
- printf("-info-:%x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + 12 + seeker) & 0xffff);
+ debugN("Name: %s\n", namepos ? ((char *)data + namepos) : "<unknown>");
+ debugN("Superclass: %x\n", superclass);
+ debugN("Species: %x\n", species);
+ debugN("-info-:%x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + 12 + seeker) & 0xffff);
- printf("Function area offset: %x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + 4));
- printf("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + 6)));
+ debugN("Function area offset: %x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + 4));
+ debugN("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + 6)));
seeker += 8;
selectorsize <<= 1;
@@ -413,7 +420,7 @@ void Kernel::dumpScriptClass(char *data, int seeker, int objsize) {
while (selectors--) {
int selector = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + (seeker) + selectorsize);
- printf(" [%03x] %s = 0x%x\n", 0xffff & selector, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>",
+ debugN(" [%03x] %s = 0x%x\n", 0xffff & selector, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>",
(int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker) & 0xffff);
seeker += 2;
@@ -421,16 +428,16 @@ void Kernel::dumpScriptClass(char *data, int seeker, int objsize) {
seeker += selectorsize;
- printf("Overloaded functions: %x\n", selectors = overloads = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker));
+ debugN("Overloaded functions: %x\n", selectors = overloads = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker));
seeker += 2;
while (overloads--) {
int selector = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + (seeker));
- fprintf(stderr, "selector=%d; selectorNames.size() =%d\n", selector, _selectorNames.size());
- printf(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ?
+ debugN("selector=%d; selectorNames.size() =%d\n", selector, _selectorNames.size());
+ debugN(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ?
_selectorNames[selector].c_str() : "<?>");
- printf("%04x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + selectors * 2 + 2) & 0xffff);
+ debugN("%04x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + selectors * 2 + 2) & 0xffff);
seeker += 2;
}
@@ -452,17 +459,17 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
unsigned int seeker = _seeker + 4;
if (!objType) {
- printf("End of script object (#0) encountered.\n");
- printf("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)",
+ debugN("End of script object (#0) encountered.\n");
+ debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)",
objectctr[6], objectctr[1], objectctr[7], objectctr[10]);
return;
}
- printf("\n");
+ debugN("\n");
objsize = (int16)READ_SCI11ENDIAN_UINT16(script->data + _seeker + 2);
- printf("Obj type #%x, size 0x%x: ", objType, objsize);
+ debugN("Obj type #%x, size 0x%x: ", objType, objsize);
_seeker += objsize;
@@ -474,28 +481,28 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
break;
case SCI_OBJ_CODE:
- printf("Code\n");
+ debugN("Code\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
break;
- case 3:
- printf("<unknown>\n");
+ case SCI_OBJ_SYNONYMS:
+ debugN("Synonyms\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
break;
case SCI_OBJ_SAID:
- printf("Said\n");
+ debugN("Said\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- printf("%04x: ", seeker);
+ debugN("%04x: ", seeker);
vocab->debugDecipherSaidBlock(script->data + seeker);
- printf("\n");
+ debugN("\n");
break;
case SCI_OBJ_STRINGS:
- printf("Strings\n");
+ debugN("Strings\n");
while (script->data [seeker]) {
- printf("%04x: %s\n", seeker, script->data + seeker);
+ debugN("%04x: %s\n", seeker, script->data + seeker);
seeker += strlen((char *)script->data + seeker) + 1;
}
seeker++; // the ending zero byte
@@ -506,33 +513,33 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
break;
case SCI_OBJ_EXPORTS:
- printf("Exports\n");
+ debugN("Exports\n");
Common::hexdump((unsigned char *)script->data + seeker, objsize - 4, 16, seeker);
break;
case SCI_OBJ_POINTERS:
- printf("Pointers\n");
+ debugN("Pointers\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
break;
case 9:
- printf("<unknown>\n");
+ debugN("<unknown>\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
break;
case SCI_OBJ_LOCALVARS:
- printf("Local vars\n");
+ debugN("Local vars\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
break;
default:
- printf("Unsupported!\n");
+ debugN("Unsupported!\n");
return;
}
}
- printf("Script ends without terminator\n");
+ debugN("Script ends without terminator\n");
}
} // End of namespace Sci
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 1cbe9a56f4..d509046a15 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -301,9 +301,9 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
return NULL_REG;
if (result.size() > 1 && index < 0) {
- printf("Ambiguous:\n");
+ debug("findObjectByName(%s): multiple matches:", name.c_str());
for (i = 0; i < result.size(); i++)
- printf(" %3x: [%04x:%04x] %s\n", i, PRINT_REG(result[i]), name.c_str());
+ debug(" %3x: [%04x:%04x]", i, PRINT_REG(result[i]));
return NULL_REG; // Ambiguous
}
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index 59ac6f39b6..55dd39d89b 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -92,11 +92,6 @@ public:
void deallocateScript(int script_nr);
/**
- * Reconstructs scripts. Used when restoring saved games
- */
- void reconstructScripts(EngineState *s);
-
- /**
* Reconstructs the stack. Used when restoring saved games
*/
void reconstructStack(EngineState *s);
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index b16dd5a5e5..e40777755f 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -26,7 +26,9 @@
#include "common/endian.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/features.h"
+#include "sci/engine/object.h"
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
#include "sci/engine/segment.h"
#include "sci/engine/seg_manager.h"
@@ -130,30 +132,6 @@ const char *SegmentObj::getSegmentTypeName(SegmentType type) {
return NULL;
}
-// This helper function is used by Script::relocateLocal and Object::relocate
-// Duplicate in segment.cpp and script.cpp
-static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
- int rel = location - block_location;
-
- if (rel < 0)
- return false;
-
- uint idx = rel >> 1;
-
- if (idx >= block.size())
- return false;
-
- if (rel & 1) {
- error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
- return false;
- }
- block[idx].segment = segment; // Perform relocation
- if (getSciVersion() >= SCI_VERSION_1_1)
- block[idx].offset += scriptSize;
-
- return true;
-}
-
SegmentRef SegmentObj::dereference(reg_t pointer) {
error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment",
PRINT_REG(pointer));
@@ -318,10 +296,8 @@ reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const {
Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const {
Common::Array<reg_t> tmp;
- fprintf(stderr, "Emitting %d stack entries\n", _capacity);
for (int i = 0; i < _capacity; i++)
tmp.push_back(_entries[i]);
- fprintf(stderr, "DONE");
return tmp;
}
@@ -371,117 +347,6 @@ Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const {
return tmp;
}
-
-//-------------------- hunk --------------------
-
-//-------------------- object ----------------------------
-
-void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
- byte *data = buf + obj_pos.offset;
- _baseObj = data;
- _pos = obj_pos;
-
- if (getSciVersion() < SCI_VERSION_1_1) {
- _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter));
- _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2);
- _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea));
- _methodCount = READ_LE_UINT16(_baseMethod - 1);
- } else {
- _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
- _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
- _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6));
- _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod);
- }
-
- if (initVariables) {
- for (uint i = 0; i < _variables.size(); i++)
- _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
- }
-}
-
-const Object *Object::getClass(SegManager *segMan) const {
- return isClass() ? this : segMan->getObject(getSuperClassSelector());
-}
-
-int Object::locateVarSelector(SegManager *segMan, Selector slc) const {
- const byte *buf;
- uint varnum;
-
- if (getSciVersion() < SCI_VERSION_1_1) {
- varnum = getVarCount();
- int selector_name_offset = varnum * 2 + kOffsetSelectorSegment;
- buf = _baseObj + selector_name_offset;
- } else {
- const Object *obj = getClass(segMan);
- varnum = obj->getVariable(1).toUint16();
- buf = (const byte *)obj->_baseVars;
- }
-
- for (uint i = 0; i < varnum; i++)
- if (READ_SCI11ENDIAN_UINT16(buf + (i << 1)) == slc) // Found it?
- return i; // report success
-
- return -1; // Failed
-}
-
-bool Object::relocate(SegmentId segment, int location, size_t scriptSize) {
- return relocateBlock(_variables, getPos().offset, segment, location, scriptSize);
-}
-
-int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const {
- int selectors = getVarCount();
-
- if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) {
- error("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])",
- propertyOffset, propertyOffset >> 1, selectors - 1);
- return -1;
- }
-
- if (getSciVersion() < SCI_VERSION_1_1) {
- const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2;
- return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset);
- } else {
- const Object *obj = this;
- if (!isClass())
- obj = segMan->getObject(getSuperClassSelector());
-
- return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset);
- }
-}
-
-void Object::initSpecies(SegManager *segMan, reg_t addr) {
- uint16 speciesOffset = getSpeciesSelector().offset;
-
- if (speciesOffset == 0xffff) // -1
- setSpeciesSelector(NULL_REG); // no species
- else
- setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr));
-}
-
-void Object::initSuperClass(SegManager *segMan, reg_t addr) {
- uint16 superClassOffset = getSuperClassSelector().offset;
-
- if (superClassOffset == 0xffff) // -1
- setSuperClassSelector(NULL_REG); // no superclass
- else
- setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr));
-}
-
-bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) {
- const Object *baseObj = segMan->getObject(getSpeciesSelector());
-
- if (baseObj) {
- _variables.resize(baseObj->getVarCount());
- // Copy base from species class, as we need its selector IDs
- _baseObj = baseObj->_baseObj;
- if (doInitSuperClass)
- initSuperClass(segMan, addr);
- return true;
- }
-
- return false;
-}
-
//-------------------- dynamic memory --------------------
reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const {
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 6eca708e2e..aae4c9650c 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -27,6 +27,8 @@
#define SCI_ENGINE_SEGMENT_H
#include "common/serializer.h"
+
+#include "sci/engine/object.h"
#include "sci/engine/vm.h"
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/util.h"
@@ -203,138 +205,6 @@ public:
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
-/** Clone has been marked as 'freed' */
-enum {
- OBJECT_FLAG_FREED = (1 << 0)
-};
-
-enum infoSelectorFlags {
- kInfoFlagClone = 0x0001,
- kInfoFlagClass = 0x8000
-};
-
-enum ObjectOffsets {
- kOffsetLocalVariables = -6,
- kOffsetFunctionArea = -4,
- kOffsetSelectorCounter = -2,
- kOffsetSelectorSegment = 0,
- kOffsetInfoSelectorSci0 = 4,
- kOffsetNamePointerSci0 = 6,
- kOffsetInfoSelectorSci11 = 14,
- kOffsetNamePointerSci11 = 16
-};
-
-class Object {
-public:
- Object() {
- _offset = getSciVersion() < SCI_VERSION_1_1 ? 0 : 5;
- _flags = 0;
- _baseObj = 0;
- _baseVars = 0;
- _baseMethod = 0;
- _methodCount = 0;
- }
-
- ~Object() { }
-
- reg_t getSpeciesSelector() const { return _variables[_offset]; }
- void setSpeciesSelector(reg_t value) { _variables[_offset] = value; }
-
- reg_t getSuperClassSelector() const { return _variables[_offset + 1]; }
- void setSuperClassSelector(reg_t value) { _variables[_offset + 1] = value; }
-
- reg_t getInfoSelector() const { return _variables[_offset + 2]; }
- void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; }
-
- reg_t getNameSelector() const { return _variables[_offset + 3]; }
- void setNameSelector(reg_t value) { _variables[_offset + 3] = value; }
-
- reg_t getPropDictSelector() const { return _variables[2]; }
- void setPropDictSelector(reg_t value) { _variables[2] = value; }
-
- reg_t getClassScriptSelector() const { return _variables[4]; }
- void setClassScriptSelector(reg_t value) { _variables[4] = value; }
-
- Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
-
- reg_t getFunction(uint16 i) const {
- uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2;
- return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset));
- }
-
- Selector getFuncSelector(uint16 i) const {
- uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1;
- return READ_SCI11ENDIAN_UINT16(_baseMethod + offset);
- }
-
- /**
- * Determines if this object is a class and explicitly defines the
- * selector as a funcselector. Does NOT say anything about the object's
- * superclasses, i.e. failure may be returned even if one of the
- * superclasses defines the funcselector
- */
- int funcSelectorPosition(Selector sel) const {
- for (uint i = 0; i < _methodCount; i++)
- if (getFuncSelector(i) == sel)
- return i;
-
- return -1;
- }
-
- /**
- * Determines if the object explicitly defines slc as a varselector.
- * Returns -1 if not found.
- */
- int locateVarSelector(SegManager *segMan, Selector slc) const;
-
- bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); }
- const Object *getClass(SegManager *segMan) const;
-
- void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
- bool isFreed() const { return _flags & OBJECT_FLAG_FREED; }
-
- uint getVarCount() const { return _variables.size(); }
-
- void init(byte *buf, reg_t obj_pos, bool initVariables = true);
-
- reg_t getVariable(uint var) const { return _variables[var]; }
- reg_t &getVariableRef(uint var) { return _variables[var]; }
-
- uint16 getMethodCount() const { return _methodCount; }
- reg_t getPos() const { return _pos; }
-
- void saveLoadWithSerializer(Common::Serializer &ser);
-
- void cloneFromObject(const Object *obj) {
- _baseObj = obj ? obj->_baseObj : NULL;
- _baseMethod = obj ? obj->_baseMethod : NULL;
- _baseVars = obj ? obj->_baseVars : NULL;
- }
-
- bool relocate(SegmentId segment, int location, size_t scriptSize);
-
- int propertyOffsetToId(SegManager *segMan, int propertyOffset) const;
-
- void initSpecies(SegManager *segMan, reg_t addr);
- void initSuperClass(SegManager *segMan, reg_t addr);
- bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
-
- // TODO: make private
- // Only SegManager::reconstructScripts() is left needing direct access to these
-public:
- const byte *_baseObj; /**< base + object offset within base */
-
-private:
- const uint16 *_baseVars; /**< Pointer to the varselector area for this object */
- const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */
-
- Common::Array<reg_t> _variables;
- uint16 _methodCount;
- int _flags;
- uint16 _offset;
- reg_t _pos; /**< Object offset within its script; for clones, this is their base */
-};
-
/** Data stack */
struct DataStack : SegmentObj {
int _capacity; /**< Number of stack entries */
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index b31f52aa13..798dbf529c 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -40,10 +40,10 @@ namespace Sci {
// The defines below can be used to construct static selector tables for games which don't have
// a vocab.997 resource, by dumping the selector table from other similar versions or games
#define FIND_SELECTOR(_slc_) _selectorCache._slc_ = findSelector(#_slc_); \
- printf("\t{ \"%s\", %d },\n", #_slc_, _selectorCache._slc_)
+ debugN("\t{ \"%s\", %d },\n", #_slc_, _selectorCache._slc_)
#define FIND_SELECTOR2(_slc_, _slcstr_) _selectorCache._slc_ = findSelector(_slcstr_); \
- printf("\t{ \"%s\", %d },\n", _slcstr_, _selectorCache._slc_)
+ debugN("\t{ \"%s\", %d },\n", _slcstr_, _selectorCache._slc_)
#endif
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 98157c3eaf..6038ad0c36 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -40,7 +40,7 @@ struct SelectorCache {
}
// Statically defined selectors, (almost the) same in all SCI versions
- Selector _info_;
+ Selector _info_; ///< Removed in SCI3
Selector y;
Selector x;
Selector view, loop, cel; ///< Description of a specific image
@@ -58,8 +58,9 @@ struct SelectorCache {
// style
Selector state, font, type;///< Used by controls
// window
- Selector cursor, max; ///< Used by EditControl
- Selector mark; //< Used by list controls
+ Selector cursor; ///< Used by EditControl
+ Selector max; ///< Used by EditControl, removed in SCI3
+ Selector mark; //< Used by list controls (script internal, is needed by us for the QfG import rooms)
Selector sort; //< Used by list controls (script internal, is needed by us for QfG3 import room)
// who
Selector message; ///< Used by GetEvent
@@ -96,8 +97,8 @@ struct SelectorCache {
Selector subtitleLang;
Selector size;
Selector points; ///< Used by AvoidPath()
- Selector palette;
- Selector dataInc;
+ Selector palette; ///< Used by the SCI0-SCI1.1 animate code, unused in SCI2-SCI2.1, removed in SCI3
+ Selector dataInc; ///< Used to sync music with animations, removed in SCI3
// handle (in SCI1)
Selector min; ///< SMPTE time format
Selector sec;
@@ -109,7 +110,7 @@ struct SelectorCache {
// SCI1 selectors which have been moved a bit in SCI1.1, but otherwise static
Selector cantBeHere; ///< Checks for movement avoidance in SCI1+. Replaces canBeHere
- Selector topString; ///< SCI1 scroll lists use this instead of lsTop
+ Selector topString; ///< SCI1 scroll lists use this instead of lsTop. Removed in SCI3
Selector flags;
// SCI1+ audio sync related selectors, not static. They're used for lip syncing in
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 732f075257..886b099a26 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -307,9 +307,8 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) {
kLanguage lang = getSciLanguage();
kLanguage subLang = K_LANG_NONE;
- if (SELECTOR(subtitleLang) != -1) {
+ if (SELECTOR(subtitleLang) != -1)
subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(subtitleLang));
- }
kLanguage secondLang;
Common::String retval = getSciLanguageString(str, lang, &secondLang);
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index d0ddd5ca06..e5c9334315 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -125,7 +125,6 @@ public:
/* Non-VM information */
- uint32 gameStartTime; /**< The time at which the interpreter was started */
uint32 lastWaitTime; /**< The last time the game invoked Wait() */
uint32 _screenUpdateTime; /**< The last time the game updated the screen */
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index ed8d4193a9..4bb61a0658 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -64,6 +64,12 @@ static const char * const sci1Selectors[] = {
"frame", "vol", "pri", "perform", "moveDone" // 93 - 97
};
+static const char * const sci11Selectors[] = {
+ "topString", "flags", "quitGame", "restart", "hide", // 98 - 102
+ "scaleSignal", "scaleX", "scaleY", "maxScale","vanishingX", // 103 - 107
+ "vanishingY" // 108
+};
+
#ifdef ENABLE_SCI32
static const char * const sci2Selectors[] = {
"plane", "x", "y", "z", "scaleX", // 0 - 4
@@ -108,17 +114,6 @@ static const SelectorRemap sciSelectorRemap[] = {
// SCI1.1
{ SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
{ SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "topString", 98 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "flags", 99 },
- // quitGame
- // restart
- // hide
- { SCI_VERSION_1_1, SCI_VERSION_1_1,"scaleSignal", 103 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "scaleX", 104 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "scaleY", 105 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "maxScale", 106 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "vanishingX", 107 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "vanishingY", 108 },
{ SCI_VERSION_1_1, SCI_VERSION_2_1, "-info-",4103 },
{ SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
};
@@ -132,10 +127,12 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
#else
const int count = ARRAYSIZE(sci0Selectors) + offset;
#endif
+ int countSci1 = ARRAYSIZE(sci1Selectors);
+ int countSci11 = ARRAYSIZE(sci11Selectors);
// Resize the list of selector names and fill in the SCI 0 names.
names.resize(count);
- if (getSciVersion() < SCI_VERSION_1_1) {
+ if (getSciVersion() <= SCI_VERSION_1_LATE) {
// Fill selectors 0 - 2 for SCI0 - SCI1 late
names[0] = "species";
names[1] = "superClass";
@@ -149,12 +146,18 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
if (getSciVersion() > SCI_VERSION_01) {
// Several new selectors were added in SCI 1 and later.
- int count2 = ARRAYSIZE(sci1Selectors);
- names.resize(count + count2);
- for (int i = count; i < count + count2; i++)
+ names.resize(count + countSci1);
+ for (int i = count; i < count + countSci1; i++)
names[i] = sci1Selectors[i - count];
}
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ // Several new selectors were added in SCI 1.1
+ names.resize(count + countSci1 + countSci11);
+ for (int i = count + countSci1; i < count + countSci1 + countSci11; i++)
+ names[i] = sci11Selectors[i - count - countSci1];
+ }
+
// Now, we need to find out selectors which keep changing place...
// We do that by dissecting game objects, and looking for selectors at
// specified locations.
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 7342f8ca7b..8398b7d8c2 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -34,6 +34,7 @@
#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/object.h"
#include "sci/engine/script.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/selector.h" // for SELECTOR
@@ -94,7 +95,7 @@ static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack,
// validation functionality
-static reg_t &validate_property(Object *obj, int index) {
+static reg_t &validate_property(EngineState *s, Object *obj, int index) {
// A static dummy reg_t, which we return if obj or index turn out to be
// invalid. Note that we cannot just return NULL_REG, because client code
// may modify the value of the returned reg_t.
@@ -105,6 +106,11 @@ static reg_t &validate_property(Object *obj, int index) {
if (!obj)
error("validate_property: Sending to disposed object");
+ if (getSciVersion() == SCI_VERSION_3)
+ index = obj->locateVarSelector(s->_segMan, index);
+ else
+ index >>= 1;
+
if (index < 0 || (uint)index >= obj->getVarCount()) {
// This is same way sierra does it and there are some games, that contain such scripts like
// iceman script 998 (fred::canBeHere, executed right at the start)
@@ -144,13 +150,13 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
const char *names[4] = {"global", "local", "temp", "param"};
if (index < 0 || index >= max) {
- Common::String txt = Common::String::printf(
+ Common::String txt = Common::String::format(
"[VM] Attempt to use invalid %s variable %04x ",
names[type], index);
if (max == 0)
txt += "(variable type invalid)";
else
- txt += Common::String::printf("(out of range [%d..%d])", 0, max - 1);
+ txt += Common::String::format("(out of range [%d..%d])", 0, max - 1);
if (type == VAR_PARAM || type == VAR_TEMP) {
int total_offset = r - stack_base;
@@ -323,7 +329,11 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
scr = s->_segMan->getScript(seg);
}
- const int temp = scr->validateExportFunc(pubfunct);
+ int temp = scr->validateExportFunc(pubfunct, false);
+
+ if (getSciVersion() == SCI_VERSION_3)
+ temp += scr->getCodeBlockOffset();
+
if (!temp) {
#ifdef ENABLE_SCI32
// HACK: Temporarily switch to a warning in SCI32 games until we can figure out why Torin has
@@ -423,7 +433,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
#ifdef VM_DEBUG_SEND
- printf("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj),
+ debugN("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj),
s->_segMan->getObjectName(send_obj), selector,
g_sci->getKernel()->getSelectorName(selector).c_str());
#endif // VM_DEBUG_SEND
@@ -438,9 +448,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
#ifdef VM_DEBUG_SEND
if (argc)
- printf("Varselector: Write %04x:%04x\n", PRINT_REG(argp[1]));
+ debugN("Varselector: Write %04x:%04x\n", PRINT_REG(argp[1]));
else
- printf("Varselector: Read\n");
+ debugN("Varselector: Read\n");
#endif // VM_DEBUG_SEND
// argc == 0: read selector
@@ -496,39 +506,39 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
#ifndef VM_DEBUG_SEND
if (activeBreakpointTypes & BREAK_SELECTOREXEC) {
if (g_sci->checkSelectorBreakpoint(BREAK_SELECTOREXEC, send_obj, selector)) {
- printf("[execute selector]");
+ debugN("[execute selector]");
int displaySize = 0;
for (int argNr = 1; argNr <= argc; argNr++) {
if (argNr == 1)
- printf(" - ");
+ debugN(" - ");
reg_t curParam = argp[argNr];
if (curParam.segment) {
- printf("[%04x:%04x] ", PRINT_REG(curParam));
+ debugN("[%04x:%04x] ", PRINT_REG(curParam));
displaySize += 12;
} else {
- printf("[%04x] ", curParam.offset);
+ debugN("[%04x] ", curParam.offset);
displaySize += 7;
}
if (displaySize > 50) {
if (argNr < argc)
- printf("...");
+ debugN("...");
break;
}
}
- printf("\n");
+ debugN("\n");
}
}
#else // VM_DEBUG_SEND
if (activeBreakpointTypes & BREAK_SELECTOREXEC)
g_sci->checkSelectorBreakpoint(BREAK_SELECTOREXEC, send_obj, selector);
- printf("Funcselector(");
+ debugN("Funcselector(");
for (int i = 0; i < argc; i++) {
- printf("%04x:%04x", PRINT_REG(argp[i+1]));
+ debugN("%04x:%04x", PRINT_REG(argp[i+1]));
if (i + 1 < argc)
- printf(", ");
+ debugN(", ");
}
- printf(") at %04x:%04x\n", PRINT_REG(funcp));
+ debugN(") at %04x:%04x\n", PRINT_REG(funcp));
#endif // VM_DEBUG_SEND
{
@@ -583,7 +593,7 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t
// Returns new TOS element for the execution stack
// _localsSegment may be -1 if derived from the called object
- //printf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos, s->_executionStack.size(), origin, s->execution_stack);
+ //debug("Exec stack: [%d/%d], origin %d, at %p", s->execution_stack_pos, s->_executionStack.size(), origin, s->execution_stack);
ExecStack xstack;
@@ -630,7 +640,6 @@ static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
case SEG_TYPE_DYNMEM:
base.offset += offset;
return base;
-
default:
// FIXME: Changed this to warning, because iceman does this during dancing with girl.
// Investigate why that is so and either fix the underlying issue or implement a more
@@ -655,46 +664,46 @@ static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc,
static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *kernelSubCall, EngineState *s, int argc, reg_t *argv, reg_t result) {
Kernel *kernel = g_sci->getKernel();
if (!kernelSubCall) {
- printf("k%s: ", kernelCall->name);
+ debugN("k%s: ", kernelCall->name);
} else {
int callNameLen = strlen(kernelCall->name);
if (strncmp(kernelCall->name, kernelSubCall->name, callNameLen) == 0) {
const char *subCallName = kernelSubCall->name + callNameLen;
- printf("k%s(%s): ", kernelCall->name, subCallName);
+ debugN("k%s(%s): ", kernelCall->name, subCallName);
} else {
- printf("k%s(%s): ", kernelCall->name, kernelSubCall->name);
+ debugN("k%s(%s): ", kernelCall->name, kernelSubCall->name);
}
}
for (int parmNr = 0; parmNr < argc; parmNr++) {
if (parmNr)
- printf(", ");
+ debugN(", ");
uint16 regType = kernel->findRegType(argv[parmNr]);
if (regType & SIG_TYPE_NULL)
- printf("0");
+ debugN("0");
else if (regType & SIG_TYPE_UNINITIALIZED)
- printf("UNINIT");
+ debugN("UNINIT");
else if (regType & SIG_IS_INVALID)
- printf("INVALID");
+ debugN("INVALID");
else if (regType & SIG_TYPE_INTEGER)
- printf("%d", argv[parmNr].offset);
+ debugN("%d", argv[parmNr].offset);
else {
- printf("%04x:%04x", PRINT_REG(argv[parmNr]));
+ debugN("%04x:%04x", PRINT_REG(argv[parmNr]));
switch (regType) {
case SIG_TYPE_OBJECT:
- printf(" (%s)", s->_segMan->getObjectName(argv[parmNr]));
+ debugN(" (%s)", s->_segMan->getObjectName(argv[parmNr]));
break;
case SIG_TYPE_REFERENCE:
if (kernelCall->function == kSaid) {
SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]);
if (saidSpec.isRaw) {
- printf(" ('");
+ debugN(" ('");
g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw);
- printf("')");
+ debugN("')");
} else {
- printf(" (non-raw said-spec)");
+ debugN(" (non-raw said-spec)");
}
} else {
- printf(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str());
+ debugN(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str());
}
default:
break;
@@ -702,9 +711,9 @@ static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunct
}
}
if (result.segment)
- printf(" = %04x:%04x\n", PRINT_REG(result));
+ debugN(" = %04x:%04x\n", PRINT_REG(result));
else
- printf(" = %d\n", result.offset);
+ debugN(" = %d\n", result.offset);
}
static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
@@ -749,7 +758,7 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
if (kernelCall.debugLogging)
logKernelCall(&kernelCall, NULL, s, argc, argv, s->r_acc);
if (kernelCall.debugBreakpoint) {
- printf("Break on k%s\n", kernelCall.name);
+ debugN("Break on k%s\n", kernelCall.name);
g_sci->_debugState.debugging = true;
g_sci->_debugState.breakpointWasHit = true;
}
@@ -804,7 +813,7 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
if (kernelSubCall.debugLogging)
logKernelCall(&kernelCall, &kernelSubCall, s, argc, argv, s->r_acc);
if (kernelSubCall.debugBreakpoint) {
- printf("Break on k%s\n", kernelSubCall.name);
+ debugN("Break on k%s\n", kernelSubCall.name);
g_sci->_debugState.debugging = true;
g_sci->_debugState.breakpointWasHit = true;
}
@@ -830,7 +839,7 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
memset(opparams, 0, sizeof(opparams));
for (int i = 0; g_opcode_formats[opcode][i]; ++i) {
- //printf("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp);
+ //debugN("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp);
assert(i < 3);
switch (g_opcode_formats[opcode][i]) {
@@ -1552,7 +1561,19 @@ void run_vm(EngineState *s) {
case 0x26: // (38)
case 0x27: // (39)
- error("Dummy opcode 0x%x called", opcode); // should never happen
+ if (getSciVersion() == SCI_VERSION_3) {
+ if (extOpcode == 0x4c)
+ s->r_acc = obj->getInfoSelector();
+ else if (extOpcode == 0x4d)
+ PUSH32(obj->getInfoSelector());
+ else if (extOpcode == 0x4e)
+ s->r_acc = obj->getSuperClassSelector(); // TODO: is this correct?
+ // TODO: There are also opcodes in
+ // here to get the superclass, and possibly the species too.
+ else
+ error("Dummy opcode 0x%x called", opcode); // should never happen
+ } else
+ error("Dummy opcode 0x%x called", opcode); // should never happen
break;
case op_class: // 0x28 (40)
@@ -1650,27 +1671,27 @@ void run_vm(EngineState *s) {
case op_pToa: // 0x31 (49)
// Property To Accumulator
- s->r_acc = validate_property(obj, (opparams[0] >> 1));
+ s->r_acc = validate_property(s, obj, opparams[0]);
break;
case op_aTop: // 0x32 (50)
// Accumulator To Property
- validate_property(obj, (opparams[0] >> 1)) = s->r_acc;
+ validate_property(s, obj, opparams[0]) = s->r_acc;
break;
case op_pTos: // 0x33 (51)
// Property To Stack
- PUSH32(validate_property(obj, opparams[0] >> 1));
+ PUSH32(validate_property(s, obj, opparams[0]));
break;
case op_sTop: // 0x34 (52)
// Stack To Property
- validate_property(obj, (opparams[0] >> 1)) = POP32();
+ validate_property(s, obj, opparams[0]) = POP32();
break;
case op_ipToa: { // 0x35 (53)
// Increment Property and copy To Accumulator
- reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ reg_t &opProperty = validate_property(s, obj, opparams[0]);
uint16 valueProperty;
if (validate_unsignedInteger(opProperty, valueProperty))
s->r_acc = make_reg(0, valueProperty + 1);
@@ -1682,7 +1703,7 @@ void run_vm(EngineState *s) {
case op_dpToa: { // 0x36 (54)
// Decrement Property and copy To Accumulator
- reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ reg_t &opProperty = validate_property(s, obj, opparams[0]);
uint16 valueProperty;
if (validate_unsignedInteger(opProperty, valueProperty))
s->r_acc = make_reg(0, valueProperty - 1);
@@ -1694,7 +1715,7 @@ void run_vm(EngineState *s) {
case op_ipTos: { // 0x37 (55)
// Increment Property and push to Stack
- reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ reg_t &opProperty = validate_property(s, obj, opparams[0]);
uint16 valueProperty;
if (validate_unsignedInteger(opProperty, valueProperty))
valueProperty++;
@@ -1707,7 +1728,7 @@ void run_vm(EngineState *s) {
case op_dpTos: { // 0x38 (56)
// Decrement Property and push to Stack
- reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ reg_t &opProperty = validate_property(s, obj, opparams[0]);
uint16 valueProperty;
if (validate_unsignedInteger(opProperty, valueProperty))
valueProperty--;
@@ -1723,19 +1744,27 @@ void run_vm(EngineState *s) {
s->r_acc.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
- case SCI_VERSION_1_1:
- s->r_acc.offset = opparams[0] + local_script->getScriptSize();
+ case SCI_VERSION_0_EARLY:
+ s->r_acc.offset = s->xs->addr.pc.offset + opparams[0];
break;
case SCI_VERSION_1_MIDDLE:
s->r_acc.offset = opparams[0];
break;
+ case SCI_VERSION_1_1:
+ s->r_acc.offset = opparams[0] + local_script->getScriptSize();
+ break;
+ case SCI_VERSION_3:
+ // In theory this can break if the variant with a one-byte argument is
+ // used. For now, assume it doesn't happen.
+ s->r_acc.offset = local_script->relocateOffsetSci3(s->xs->addr.pc.offset-2);
+ break;
default:
- s->r_acc.offset = s->xs->addr.pc.offset + opparams[0];
+ error("Unknown lofs type");
}
if (s->r_acc.offset >= scr->getBufSize()) {
error("VM: lofsa operation overflowed: %04x:%04x beyond end"
- " of script (at %04x)\n", PRINT_REG(s->r_acc), scr->getBufSize());
+ " of script (at %04x)", PRINT_REG(s->r_acc), scr->getBufSize());
}
break;
@@ -1744,14 +1773,20 @@ void run_vm(EngineState *s) {
r_temp.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
+ case SCI_VERSION_0_EARLY:
+ r_temp.offset = s->xs->addr.pc.offset + opparams[0];
+ break;
+ case SCI_VERSION_1_MIDDLE:
+ r_temp.offset = opparams[0];
+ break;
case SCI_VERSION_1_1:
r_temp.offset = opparams[0] + local_script->getScriptSize();
break;
- case SCI_VERSION_1_MIDDLE:
+ case SCI_VERSION_3:
r_temp.offset = opparams[0];
break;
default:
- r_temp.offset = s->xs->addr.pc.offset + opparams[0];
+ error("Unknown lofs type");
}
if (r_temp.offset >= scr->getBufSize()) {
@@ -1774,7 +1809,13 @@ void run_vm(EngineState *s) {
break;
case op_pushSelf: // 0x3e (62)
- if (!(extOpcode & 1)) {
+ // Compensate for a bug in non-Sierra compilers, which seem to generate
+ // pushSelf instructions with the low bit set. This makes the following
+ // heuristic fail and leads to endless loops and crashes. Our
+ // interpretation of this seems correct, as other SCI tools, like for
+ // example SCI Viewer, have issues with these scripts (e.g. script 999
+ // in Circus Quest). Fixes bug #3038686.
+ if (!(extOpcode & 1) || g_sci->getGameId() == GID_FANMADE) {
PUSH32(s->xs->objp);
} else {
// Debug opcode op_file, skip null-terminated string (file name)
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index d332c64a9d..de49db1f21 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -24,6 +24,7 @@
*/
#include "sci/engine/kernel.h"
+#include "sci/engine/object.h"
#include "sci/engine/state.h"
#include "sci/engine/vm.h"
#include "sci/engine/workarounds.h"
@@ -117,8 +118,10 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts
{ GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", -1, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game
{ GID_HOYLE1, 3, 16, 0, "", "export 0", 0x37c, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #3052359
+ { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #3108012
{ GID_HOYLE3, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505
{ GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu
+ { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", -1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #3036918
{ GID_HOYLE4, -1, 0, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #3039294
{ GID_HOYLE4, 910, 18, 0, "Tray", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #3042756
{ GID_HOYLE4, 910, 910, 0, "IconBarList", "setup", -1, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #3039294
@@ -169,6 +172,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout
{ GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout
{ GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #3053268
+ { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", 0xcee, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #3109299
{ GID_QFG2, -1, 71, 0, "theInvSheet", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory
{ GID_QFG2, -1, 701, -1, "Alley", "at", -1, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #3035835 & #3038367
{ GID_QFG2, -1, 990, 0, "Restore", "doit", -1, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present
@@ -185,6 +189,10 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", -1, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen
{ GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu
{ GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happen sometimes in fights
+ { GID_RAMA, 12, 64950, -1, "InterfaceFeature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
+ { GID_RAMA, 12, 64950, -1, "hiliteOptText", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
+ { GID_RAMA, 12, 64950, -1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts
+ { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", -1, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo
{ GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2
{ GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser
{ GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens)
@@ -337,6 +345,8 @@ const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
{ GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
{ GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
{ GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0xb2, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #3094235
+ { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0x16, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the German version - bug #3116892
{ GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified
{ GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #3036331
{ GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified
@@ -404,12 +414,19 @@ const SciWorkaroundEntry kStrCat_workarounds[] = {
};
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kStrLen_workarounds[] = {
+ { GID_QFG2, 210, 2, 0, "", "export 21", 0xdeb, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #3100292
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kUnLoad_workarounds[] = {
{ GID_CAMELOT, 921, 921, 1, "Script", "changeState", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: While showing Camelot (and other places), the reference is invalid - bug #3035000
{ GID_CAMELOT, 921, 921, 1, "Script", "init", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When being attacked by the boar (and other places), the reference is invalid - bug #3035000
{ GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference
{ GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473
{ GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time
+ { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room - bug #3098353
{ GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
{ GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
{ GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902
@@ -425,6 +442,15 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = {
};
SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) {
+ // HACK for SCI3: Temporarily ignore this
+ if (getSciVersion() == SCI_VERSION_3) {
+ warning("SCI3 HACK: trackOriginAndFindWorkaround() called, ignoring");
+ SciWorkaroundSolution sci3IgnoreForNow;
+ sci3IgnoreForNow.type = WORKAROUND_FAKE;
+ sci3IgnoreForNow.value = 0;
+ return sci3IgnoreForNow;
+ }
+
EngineState *state = g_sci->getEngineState();
ExecStack *lastCall = state->xs;
Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment);
@@ -454,7 +480,7 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector);
} else if (lastCall->debugExportId != -1) {
curObjectName = "";
- curMethodName = curMethodName.printf("export %d", lastCall->debugExportId);
+ curMethodName = Common::String::format("export %d", lastCall->debugExportId);
}
}
@@ -471,8 +497,9 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
&& ((workaround->scriptNr == -1) || (workaround->scriptNr == curScriptNr))
&& ((workaround->roomNr == -1) || (workaround->roomNr == curRoomNumber))
&& ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel))
- && ((workaround->objectName == NULL) || (workaround->objectName == searchObjectName))
- && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset
+ && ((workaround->objectName == NULL) || (workaround->objectName == g_sci->getSciLanguageString(searchObjectName.c_str(), K_LANG_ENGLISH)))
+ && workaround->methodName == g_sci->getSciLanguageString(curMethodName.c_str(), K_LANG_ENGLISH)
+ && workaround->localCallOffset == lastCall->debugLocalCallOffset
&& ((workaround->index == -1) || (workaround->index == index))) {
// Workaround found
return workaround->newValue;
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
index bf1ac3a445..7d68d9c4a5 100644
--- a/engines/sci/engine/workarounds.h
+++ b/engines/sci/engine/workarounds.h
@@ -104,6 +104,7 @@ extern const SciWorkaroundEntry kSetCursor_workarounds[];
extern const SciWorkaroundEntry kSetPort_workarounds[];
extern const SciWorkaroundEntry kStrAt_workarounds[];
extern const SciWorkaroundEntry kStrCat_workarounds[];
+extern const SciWorkaroundEntry kStrLen_workarounds[];
extern const SciWorkaroundEntry kUnLoad_workarounds[];
extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin);
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 5d469eda7b..0c17db6028 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -66,10 +66,10 @@ struct ScancodeRow {
const char *keys;
};
-static const ScancodeRow s_scancodeRows[] = {
- {0x10, "QWERTYUIOP[]"},
- {0x1e, "ASDFGHJKL;'\\"},
- {0x2c, "ZXCVBNM,./"}
+const ScancodeRow s_scancodeRows[] = {
+ { 0x10, "QWERTYUIOP[]" },
+ { 0x1e, "ASDFGHJKL;'\\" },
+ { 0x2c, "ZXCVBNM,./" }
};
static int altify(int ch) {
@@ -93,38 +93,7 @@ static int altify(int ch) {
return ch;
}
-/*
-static int numlockify(int c) {
- switch (c) {
- case SCI_KEY_DELETE:
- return '.';
- case SCI_KEY_INSERT:
- return '0';
- case SCI_KEY_END:
- return '1';
- case SCI_KEY_DOWN:
- return '2';
- case SCI_KEY_PGDOWN:
- return '3';
- case SCI_KEY_LEFT:
- return '4';
- case SCI_KEY_CENTER:
- return '5';
- case SCI_KEY_RIGHT:
- return '6';
- case SCI_KEY_HOME:
- return '7';
- case SCI_KEY_UP:
- return '8';
- case SCI_KEY_PGUP:
- return '9';
- default:
- return c; // Unchanged
- }
-}
-*/
-
-static const byte codepagemap_88591toDOS[0x80] = {
+const byte codepagemap_88591toDOS[0x80] = {
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
'?', 0xad, 0x9b, 0x9c, '?', 0x9d, '?', 0x9e, '?', '?', 0xa6, 0xae, 0xaa, '?', '?', '?', // 0xAx
@@ -135,6 +104,42 @@ static const byte codepagemap_88591toDOS[0x80] = {
'?', 0xa4, 0x95, 0xa2, 0x93, '?', 0x94, '?', '?', 0x97, 0xa3, 0x96, 0x81, '?', '?', 0x98 // 0xFx
};
+struct SciKeyConversion {
+ Common::KeyCode scummVMKey;
+ int sciKeyNumlockOff;
+ int sciKeyNumlockOn;
+};
+
+const SciKeyConversion keyMappings[] = {
+ { Common::KEYCODE_UP , SCI_KEY_UP , SCI_KEY_UP },
+ { Common::KEYCODE_DOWN , SCI_KEY_DOWN , SCI_KEY_DOWN },
+ { Common::KEYCODE_RIGHT , SCI_KEY_RIGHT , SCI_KEY_RIGHT },
+ { Common::KEYCODE_LEFT , SCI_KEY_LEFT , SCI_KEY_LEFT },
+ { Common::KEYCODE_INSERT , SCI_KEY_INSERT , SCI_KEY_INSERT },
+ { Common::KEYCODE_HOME , SCI_KEY_HOME , SCI_KEY_HOME },
+ { Common::KEYCODE_END , SCI_KEY_END , SCI_KEY_END },
+ { Common::KEYCODE_PAGEUP , SCI_KEY_PGUP , SCI_KEY_PGUP },
+ { Common::KEYCODE_PAGEDOWN , SCI_KEY_PGDOWN , SCI_KEY_PGDOWN },
+ { Common::KEYCODE_DELETE , SCI_KEY_DELETE , SCI_KEY_DELETE },
+ // Keypad
+ { Common::KEYCODE_KP0 , SCI_KEY_INSERT , '0' },
+ { Common::KEYCODE_KP1 , SCI_KEY_END , '1' },
+ { Common::KEYCODE_KP2 , SCI_KEY_DOWN , '2' },
+ { Common::KEYCODE_KP3 , SCI_KEY_PGDOWN , '3' },
+ { Common::KEYCODE_KP4 , SCI_KEY_LEFT , '4' },
+ { Common::KEYCODE_KP5 , SCI_KEY_CENTER , '5' },
+ { Common::KEYCODE_KP6 , SCI_KEY_RIGHT , '6' },
+ { Common::KEYCODE_KP7 , SCI_KEY_HOME , '7' },
+ { Common::KEYCODE_KP8 , SCI_KEY_UP , '8' },
+ { Common::KEYCODE_KP9 , SCI_KEY_PGUP , '9' },
+ { Common::KEYCODE_KP_PERIOD , SCI_KEY_DELETE , '.' },
+ { Common::KEYCODE_KP_ENTER , SCI_KEY_ENTER , SCI_KEY_ENTER },
+ { Common::KEYCODE_KP_PLUS , '+' , '+' },
+ { Common::KEYCODE_KP_MINUS , '-' , '-' },
+ { Common::KEYCODE_KP_MULTIPLY , '*' , '*' },
+ { Common::KEYCODE_KP_DIVIDE , '/' , '/' },
+};
+
SciEvent EventManager::getScummVMEvent() {
SciEvent input = { SCI_EVENT_NONE, 0, 0, 0 };
@@ -151,6 +156,7 @@ SciEvent EventManager::getScummVMEvent() {
if (found && ev.type != Common::EVENT_MOUSEMOVE) {
int modifiers = em->getModifierState();
+ bool numlockOn = (ev.kbd.flags & Common::KBD_NUM);
// We add the modifier key status to buckybits
//TODO: SCI_EVM_INSERT
@@ -159,7 +165,6 @@ SciEvent EventManager::getScummVMEvent() {
((modifiers & Common::KBD_ALT) ? SCI_KEYMOD_ALT : 0) |
((modifiers & Common::KBD_CTRL) ? SCI_KEYMOD_CTRL : 0) |
((modifiers & Common::KBD_SHIFT) ? SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT : 0) |
- ((ev.kbd.flags & Common::KBD_NUM) ? SCI_KEYMOD_NUMLOCK : 0) |
((ev.kbd.flags & Common::KBD_CAPS) ? SCI_KEYMOD_CAPSLOCK : 0) |
((ev.kbd.flags & Common::KBD_SCRL) ? SCI_KEYMOD_SCRLOCK : 0) |
_modifierStates;
@@ -200,10 +205,7 @@ SciEvent EventManager::getScummVMEvent() {
input.modifiers = 0;
return input;
}
- // we get 8859-1 character, we need dos (cp850/437) character for multilingual sci01 games
- // TODO: check, if we get 8859-1 on all platforms
- // Currently checked: Windows XP (works), Wii w/ USB keyboard (works), Mac OS X (works)
- // Ubuntu (works)
+ // We get a 8859-1 character, we need dos (cp850/437) character for multilingual sci01 games
input.character = codepagemap_88591toDOS[input.character & 0x7f];
}
if (input.data == Common::KEYCODE_TAB) {
@@ -233,77 +235,11 @@ SciEvent EventManager::getScummVMEvent() {
} else {
// Special keys that need conversion
input.type = SCI_EVENT_KEYBOARD;
- switch (ev.kbd.keycode) {
- case Common::KEYCODE_UP:
- input.data = SCI_KEY_UP;
- break;
- case Common::KEYCODE_DOWN:
- input.data = SCI_KEY_DOWN;
- break;
- case Common::KEYCODE_RIGHT:
- input.data = SCI_KEY_RIGHT;
- break;
- case Common::KEYCODE_LEFT:
- input.data = SCI_KEY_LEFT;
- break;
- case Common::KEYCODE_INSERT:
- input.data = SCI_KEY_INSERT;
- break;
- case Common::KEYCODE_HOME:
- input.data = SCI_KEY_HOME;
- break;
- case Common::KEYCODE_END:
- input.data = SCI_KEY_END;
- break;
- case Common::KEYCODE_PAGEUP:
- input.data = SCI_KEY_PGUP;
- break;
- case Common::KEYCODE_PAGEDOWN:
- input.data = SCI_KEY_PGDOWN;
- break;
- case Common::KEYCODE_DELETE:
- input.data = SCI_KEY_DELETE;
- break;
- // Keypad keys
- case Common::KEYCODE_KP8: // up
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_UP;
- break;
- case Common::KEYCODE_KP2: // down
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_DOWN;
- break;
- case Common::KEYCODE_KP6: // right
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_RIGHT;
- break;
- case Common::KEYCODE_KP4: // left
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_LEFT;
- break;
- case Common::KEYCODE_KP5: // center
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_CENTER;
- break;
- case Common::KEYCODE_KP7: // home
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_HOME;
- break;
- case Common::KEYCODE_KP9: // pageup
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_PGUP;
- break;
- case Common::KEYCODE_KP1: // end
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_END;
- break;
- case Common::KEYCODE_KP3: // pagedown
- if (!(_modifierStates & SCI_KEYMOD_NUMLOCK))
- input.data = SCI_KEY_PGDOWN;
- break;
- default:
- input.type = SCI_EVENT_NONE;
- break;
+ for (int i = 0; i < ARRAYSIZE(keyMappings); i++) {
+ if (keyMappings[i].scummVMKey == ev.kbd.keycode) {
+ input.data = numlockOn ? keyMappings[i].sciKeyNumlockOn : keyMappings[i].sciKeyNumlockOff;
+ break;
+ }
}
input.character = input.data;
}
@@ -344,7 +280,7 @@ SciEvent EventManager::getScummVMEvent() {
break;
}
}
-
+
return input;
}
@@ -419,11 +355,6 @@ SciEvent EventManager::getSciEvent(unsigned int mask) {
event.character += 96; // 0x01 -> 'a'
}
}
-
- // Numlockify if appropriate
- //if (event.modifiers & SCI_KEYMOD_NUMLOCK)
- // event.data = numlockify(event.data);
- // TODO: dont know yet if this can get dumped as well
}
return event;
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 62c5f9c19e..92bdbd30c3 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -219,7 +219,7 @@ void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view)
writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), entry->scaleY);
}
-void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
+void GfxAnimate::fill(byte &old_picNotValid) {
reg_t curObject;
uint16 signal;
GfxView *view = NULL;
@@ -274,7 +274,7 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
//warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal);
- bool setNsRect = maySetNsRect;
+ bool setNsRect = true;
// Create rect according to coordinates and given cel
if (it->scaleSignal & kScaleSignalDoScaling) {
@@ -283,18 +283,20 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
if ((signal & kSignalHidden) && !(signal & kSignalAlwaysUpdate))
setNsRect = false;
} else {
- view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be
+ // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200)
+ if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) {
+ it->celRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft));
+ it->celRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop));
+ it->celRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight));
+ it->celRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom));
+ view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ setNsRect = false;
+ } else {
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ }
}
- // This statement must be here for Hoyle4, otherwise cards are unclickable.
- // This is probably one of the experimental features that were occasionally
- // added to SCI interpreters; the corresponding check is absent in many SSCI
- // versions. m_kiewitz knew about this flag before I (lskovlun) implemented it,
- // so it is possible that more test cases are known. Also, some presently open
- // SCI1.1 bugs may be fixed by this and should be re-tested with this patch generalized.
- if (it->scaleSignal & kScaleSignalDontSetNsrect)
- setNsRect = false;
-
if (setNsRect) {
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
@@ -562,7 +564,7 @@ void GfxAnimate::addToPicDrawCels() {
// Get the corresponding view
view = _cache->getView(it->viewId);
- // kAddToPic does not do loop/cel-number fixups, it also doesn't support global scaling
+ // kAddToPic does not do loop/cel-number fixups
if (it->priority == -1)
it->priority = _ports->kernelCoordinateToPriority(it->y);
@@ -575,7 +577,9 @@ void GfxAnimate::addToPicDrawCels() {
// Create rect according to coordinates and given cel
if (it->scaleSignal & kScaleSignalDoScaling) {
- applyGlobalScaling(it, view);
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ applyGlobalScaling(it, view);
+ }
view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
@@ -655,7 +659,7 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
disposeLastCast();
makeSortedList(list);
- fill(old_picNotValid, true);
+ fill(old_picNotValid);
if (old_picNotValid) {
// beginUpdate()/endUpdate() were introduced SCI1.
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 23e7a624d8..7b5ec8ee9b 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -51,9 +51,9 @@ enum ViewSignals {
};
enum ViewScaleSignals {
- kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
- kScaleSignalGlobalScaling = 0x0002, // means that global scaling shall get applied on that cel (sets scaleX/scaleY)
- kScaleSignalDontSetNsrect = 0x0004 // do not set nsRect inside kAnimate(); for a test case see bug #3038424
+ kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
+ kScaleSignalGlobalScaling = 0x0002, // means that global scaling shall get applied on that cel (sets scaleX/scaleY)
+ kScaleSignalHoyle4SpecialHandling = 0x0004 // HOYLE4-exclusive: special handling inside kAnimate, is used when giving out cards
};
@@ -97,7 +97,7 @@ public:
bool invoke(List *list, int argc, reg_t *argv);
void makeSortedList(List *list);
void applyGlobalScaling(AnimateList::iterator entry, GfxView *view);
- void fill(byte &oldPicNotValid, bool maySetNsRect);
+ void fill(byte &oldPicNotValid);
void update();
void drawCels();
void updateScreen(byte oldPicNotValid);
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 7a37d7e865..30fc7a0f3d 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "common/config-manager.h"
#include "common/events.h"
#include "common/macresman.h"
#include "common/system.h"
@@ -59,6 +60,10 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc
_zoomColor = 0;
_zoomMultiplier = 0;
_cursorSurface = 0;
+ if (g_sci && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformWindows)
+ _useOriginalKQ6WinCursors = ConfMan.getBool("windows_cursors");
+ else
+ _useOriginalKQ6WinCursors = false;
}
GfxCursor::~GfxCursor() {
@@ -171,6 +176,10 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
if (_cachedCursors.size() >= MAX_CACHED_CURSORS)
purgeCache();
+ // Use the original Windows cursors in KQ6, if requested
+ if (_useOriginalKQ6WinCursors)
+ viewNum += 2000; // Windows cursors
+
if (!_cachedCursors.contains(viewNum))
_cachedCursors[viewNum] = new GfxView(_resMan, _screen, _palette, viewNum);
@@ -195,7 +204,7 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
}
const byte *rawBitmap = cursorView->getBitmap(loopNum, celNum);
- if (_upscaledHires) {
+ if (_upscaledHires && !_useOriginalKQ6WinCursors) {
// Scale cursor by 2x - note: sierra didn't do this, but it looks much better
width *= 2;
height *= 2;
@@ -226,6 +235,7 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
}
// TODO: What about the 2000 resources? Inventory items? How to handle?
+ // Update: Perhaps these are handled like the Windows cursors in KQ6?
// TODO: 1000 + celNum won't work for GK1
Resource *resource = _resMan->findResource(ResourceId(kResourceTypeCursor, 1000 + celNum), false);
@@ -281,10 +291,10 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// ffs. GfxCursor::setPosition (below)
// Game, newPosition, validRect
static const SciCursorSetPositionWorkarounds setPositionWorkarounds[] = {
- { GID_ISLANDBRAIN, 84, 109, 46, 76, 174, 243 }, // island of dr. brain / game menu
- { GID_LSL5, 23, 171, 0, 0, 26, 320 }, // larry 5 / skip forward helper
- { GID_QFG1VGA, 64, 174, 40, 37, 74, 284 }, // Quest For Glory 1 VGA / run/walk/sleep sub-menu
- { (SciGameId)0, -1, -1, -1, -1, -1, -1 }
+ { GID_ISLANDBRAIN, 84, 109, 46, 76, 174, 243 }, // island of dr. brain / game menu
+ { GID_LSL5, 23, 171, 0, 0, 26, 320 }, // larry 5 / skip forward helper
+ { GID_QFG1VGA, 64, 174, 40, 37, 74, 284 }, // Quest For Glory 1 VGA / run/walk/sleep sub-menu
+ { (SciGameId)0, -1, -1, -1, -1, -1, -1 }
};
void GfxCursor::setPosition(Common::Point pos) {
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index ae3b51e26a..cb65398f4b 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -112,6 +112,13 @@ private:
CursorCache _cachedCursors;
bool _isVisible;
+
+ // KQ6 Windows has different black and white cursors. If this is
+ // true (set from the sci_originalkq6wincursors ini setting), then
+ // we use these, and don't scale them by 2x like the rest of the
+ // graphics, like SSCI did. These look very ugly, which is why
+ // they aren't enabled by default.
+ bool _useOriginalKQ6WinCursors;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index 852771d081..3a6ab75cd0 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -88,7 +88,7 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
byte *pIn = getCharData(chr);
for (int i = 0; i < charHeight; i++, y++) {
if (greyedOutput)
- mask = greyedTop++ % 2 ? 0xAA : 0x55;
+ mask = ((greyedTop++) % 2) ? 0xAA : 0x55;
for (int done = 0; done < charWidth; done++) {
if ((done & 7) == 0) // fetching next data byte
b = *(pIn++) & mask;
@@ -99,4 +99,32 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
}
}
+#ifdef ENABLE_SCI32
+
+void GfxFontFromResource::drawToBuffer(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput, byte *buffer, int16 bufWidth, int16 bufHeight) {
+ int charWidth = MIN<int>(getCharWidth(chr), bufWidth - left);
+ int charHeight = MIN<int>(getCharHeight(chr), bufHeight - top);
+ byte b = 0, mask = 0xFF;
+ int y = 0;
+ int16 greyedTop = top;
+
+ byte *pIn = getCharData(chr);
+ for (int i = 0; i < charHeight; i++, y++) {
+ if (greyedOutput)
+ mask = ((greyedTop++) % 2) ? 0xAA : 0x55;
+ for (int done = 0; done < charWidth; done++) {
+ if ((done & 7) == 0) // fetching next data byte
+ b = *(pIn++) & mask;
+ if (b & 0x80) { // if MSB is set - paint it
+ _screen->putFontPixel(top, left + done, y, color);
+ int offset = (top + y) * bufWidth + (left + done);
+ buffer[offset] = color;
+ }
+ b = b << 1;
+ }
+ }
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h
index b9bee0fa9e..d8afb73a73 100644
--- a/engines/sci/graphics/font.h
+++ b/engines/sci/graphics/font.h
@@ -56,6 +56,10 @@ public:
byte getHeight();
byte getCharWidth(uint16 chr);
void draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput);
+#ifdef ENABLE_SCI32
+ // SCI2/2.1 equivalent
+ void drawToBuffer(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput, byte *buffer, int16 width, int16 height);
+#endif
private:
byte getCharHeight(uint16 chr);
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index fc374ea143..2c00f23066 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -44,6 +44,8 @@
namespace Sci {
+// TODO/FIXME: This is all guesswork
+
GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette *palette, GfxPaint32 *paint32)
: _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) {
@@ -61,8 +63,15 @@ void GfxFrameout::kernelAddPlane(reg_t object) {
if (_planes.empty()) {
// There has to be another way for sierra sci to do this or maybe script resolution is compiled into
// interpreter (TODO)
- scriptsRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
- scriptsRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
+ uint16 tmpRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
+ uint16 tmpRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
+
+ // The above can be 0 in SCI3 (e.g. Phantasmagoria 2)
+ if (tmpRunningWidth > 0 && tmpRunningHeight > 0) {
+ scriptsRunningWidth = tmpRunningWidth;
+ scriptsRunningHeight = tmpRunningHeight;
+ }
+
_coordAdjuster->setScriptsResolution(scriptsRunningWidth, scriptsRunningHeight);
}
@@ -130,6 +139,15 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
it->planeBack = readSelectorValue(_segMan, object, SELECTOR(back));
sortPlanes();
+
+ // 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);
+ }
+ }
+
return;
}
}
@@ -186,17 +204,50 @@ void GfxFrameout::deletePlanePictures(reg_t object) {
}
void GfxFrameout::kernelAddScreenItem(reg_t object) {
- _screenItems.push_back(object);
+ // Ignore invalid items
+ if (!_segMan->isObject(object))
+ return;
+
+ FrameoutEntry *itemEntry = new FrameoutEntry();
+ itemEntry->object = object;
+ itemEntry->givenOrderNr = _screenItems.size();
+ _screenItems.push_back(itemEntry);
+
+ kernelUpdateScreenItem(object);
}
void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
- // TODO
+ // Ignore invalid items
+ if (!_segMan->isObject(object))
+ return;
+
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ FrameoutEntry *itemEntry = *listIterator;
+
+ if (itemEntry->object == object) {
+ 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;
+
+ itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal));
+ itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
+ itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
+ return;
+ }
+ }
}
void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
- for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
- if (_screenItems[itemNr] == object) {
- _screenItems.remove_at(itemNr);
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ FrameoutEntry *itemEntry = *listIterator;
+ if (itemEntry->object == object) {
+ _screenItems.remove(itemEntry);
return;
}
}
@@ -207,7 +258,7 @@ int16 GfxFrameout::kernelGetHighPlanePri() {
return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority));
}
-// No idea yet how to implement this
+// TODO: No idea yet how to implement this
void GfxFrameout::kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId) {
addPlanePicture(planeObj, pictureId, forWidth);
}
@@ -252,11 +303,6 @@ void GfxFrameout::sortPlanes() {
void GfxFrameout::kernelFrameout() {
_palette->palVaryUpdate();
- // Allocate enough space for all screen items
- // TODO: Modify _screenItems to hold FrameoutEntry entries instead.
- // Creating and destroying this in kernelFrameout() is overkill!
- FrameoutEntry *itemData = new FrameoutEntry[_screenItems.size()];
-
for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
reg_t planeObject = it->object;
uint16 planeLastPriority = it->lastPriority;
@@ -280,43 +326,14 @@ void GfxFrameout::kernelFrameout() {
_coordAdjuster->pictureSetDisplayArea(it->planeRect);
_palette->drewPicture(planeMainPictureId);
- // Fill our itemlist for this plane
- int16 itemCount = 0;
- FrameoutEntry *itemEntry = itemData;
FrameoutList itemList;
- for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
- reg_t itemObject = _screenItems[itemNr];
-
- // Remove any invalid items
- if (!_segMan->isObject(itemObject)) {
- _screenItems.remove_at(itemNr);
- itemNr--;
- continue;
- }
-
- reg_t itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane));
+ // 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) {
- // Found an item on current plane
- itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view));
- itemEntry->loopNo = readSelectorValue(_segMan, itemObject, SELECTOR(loop));
- itemEntry->celNo = readSelectorValue(_segMan, itemObject, SELECTOR(cel));
- itemEntry->x = readSelectorValue(_segMan, itemObject, SELECTOR(x));
- itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y));
- itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z));
- itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority));
- if (readSelectorValue(_segMan, itemObject, SELECTOR(fixPriority)) == 0)
- itemEntry->priority = itemEntry->y;
-
- itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal));
- itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX));
- itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY));
- itemEntry->object = itemObject;
- itemEntry->givenOrderNr = itemNr;
-
- itemList.push_back(itemEntry);
- itemEntry++;
- itemCount++;
+ kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary?
+ itemList.push_back(*listIterator);
}
}
@@ -348,13 +365,10 @@ void GfxFrameout::kernelFrameout() {
// Now sort our itemlist
Common::sort(itemList.begin(), itemList.end(), sortHelper);
- // Now display itemlist
- itemEntry = itemData;
-
// warning("Plane %s", _segMan->getObjectName(planeObject));
for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
- itemEntry = *listIterator;
+ FrameoutEntry *itemEntry = *listIterator;
if (itemEntry->object.isNull()) {
// Picture cel data
@@ -530,7 +544,6 @@ void GfxFrameout::kernelFrameout() {
}
}
- delete[] itemData;
_screen->copyToScreen();
g_sci->getEngineState()->_throttleTrigger = true;
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 07297a91af..bd708dbc79 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -109,7 +109,7 @@ private:
GfxScreen *_screen;
GfxPaint32 *_paint32;
- Common::Array<reg_t> _screenItems;
+ Common::List<FrameoutEntry *> _screenItems;
PlaneList _planes;
PlanePictureList _planePictures;
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index 4b4cd673b4..f6cb214a2b 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -64,7 +64,7 @@ struct Port {
}
};
-struct Window : public Port {
+struct Window : public Port, public Common::Serializable {
Common::Rect dims; // client area of window
Common::Rect restoreRect; // total area of window including borders
uint16 wndStyle;
@@ -79,6 +79,40 @@ struct Window : public Port {
hSaved1(NULL_REG), hSaved2(NULL_REG),
bDrawn(false) {
}
+
+ void syncRect(Common::Serializer &ser, Common::Rect &targetRect) {
+ ser.syncAsSint16LE(targetRect.top);
+ ser.syncAsSint16LE(targetRect.left);
+ ser.syncAsSint16LE(targetRect.bottom);
+ ser.syncAsSint16LE(targetRect.right);
+ }
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser) {
+ ser.syncAsUint16LE(id);
+ ser.syncAsSint16LE(top);
+ ser.syncAsSint16LE(left);
+ syncRect(ser, rect);
+ ser.syncAsSint16LE(curTop);
+ ser.syncAsSint16LE(curLeft);
+ ser.syncAsSint16LE(fontHeight);
+ ser.syncAsSint32LE(fontId);
+ ser.syncAsByte(greyedOutput);
+ ser.syncAsSint16LE(penClr);
+ ser.syncAsSint16LE(backClr);
+ ser.syncAsSint16LE(penMode);
+ ser.syncAsUint16LE(counterTillFree);
+ syncRect(ser, dims);
+ syncRect(ser, restoreRect);
+ ser.syncAsUint16LE(wndStyle);
+ ser.syncAsUint16LE(saveScreenMask);
+ if (ser.isLoading()) {
+ // The hunk table isn't saved, so we just set both pointers to NULL
+ hSaved1 = NULL_REG;
+ hSaved2 = NULL_REG;
+ }
+ ser.syncString(title);
+ ser.syncAsByte(bDrawn);
+ }
};
struct Color {
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index 6f2c9596db..2ce17ab531 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -30,7 +30,7 @@
#include "sci/graphics/maciconbar.h"
#include "sci/graphics/palette.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "graphics/pict.h"
#include "graphics/surface.h"
@@ -54,7 +54,7 @@ void GfxMacIconBar::drawIcons() {
if (!res)
continue;
- Common::MemoryReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
Graphics::Surface *surf = pict->decodeImage(stream, pal);
remapColors(surf, pal);
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 06470bc560..92644db037 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -296,7 +296,7 @@ void GfxMenu::kernelSetAttribute(uint16 menuId, uint16 itemId, uint16 attributeI
itemEntry->keyPress = tolower(value.offset);
itemEntry->keyModifier = 0;
// TODO: Find out how modifier is handled
- printf("setAttr keypress %X %X\n", value.segment, value.offset);
+ debug("setAttr keypress %X %X", value.segment, value.offset);
break;
case SCI_MENU_ATTRIBUTE_TAG:
itemEntry->tag = value.offset;
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 76b2ed53fc..a0b1323eb3 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -192,6 +192,16 @@ void GfxPalette::modifyAmigaPalette(byte *data) {
_screen->setPalette(&_sysPalette);
}
+static byte blendColors(byte c1, byte c2) {
+ // linear
+ // return (c1/2+c2/2)+((c1&1)+(c2&1))/2;
+
+ // gamma 2.2
+ double t = (pow(c1/255.0, 2.2/1.0) * 255.0) +
+ (pow(c2/255.0, 2.2/1.0) * 255.0);
+ return (byte)(0.5 + (pow(0.5*t/255.0, 1.0/2.2) * 255.0));
+}
+
void GfxPalette::setEGA() {
int curColor;
byte color1, color2;
@@ -219,9 +229,10 @@ void GfxPalette::setEGA() {
for (curColor = 0x10; curColor <= 0xFE; curColor++) {
_sysPalette.colors[curColor].used = 1;
color1 = curColor & 0x0F; color2 = curColor >> 4;
- _sysPalette.colors[curColor].r = (_sysPalette.colors[color1].r >> 1) + (_sysPalette.colors[color2].r >> 1);
- _sysPalette.colors[curColor].g = (_sysPalette.colors[color1].g >> 1) + (_sysPalette.colors[color2].g >> 1);
- _sysPalette.colors[curColor].b = (_sysPalette.colors[color1].b >> 1) + (_sysPalette.colors[color2].b >> 1);
+
+ _sysPalette.colors[curColor].r = blendColors(_sysPalette.colors[color1].r, _sysPalette.colors[color2].r);
+ _sysPalette.colors[curColor].g = blendColors(_sysPalette.colors[color1].g, _sysPalette.colors[color2].g);
+ _sysPalette.colors[curColor].b = blendColors(_sysPalette.colors[color1].b, _sysPalette.colors[color2].b);
}
_sysPalette.timestamp = 1;
setOnScreen();
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 39666b82cb..707096740a 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -754,6 +754,19 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
// Dithering EGA pictures
if (isEGA) {
_screen->dither(_addToFlag);
+ switch (g_sci->getGameId()) {
+ case GID_SQ3:
+ switch (_resourceId) {
+ case 154: // SQ3: intro, ship gets sucked in
+ _screen->ditherForceMemorial(0xD0);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
return;
default:
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index e7f319a25c..ef57e4014f 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -38,15 +38,6 @@
namespace Sci {
-// window styles
-enum {
- SCI_WINDOWMGR_STYLE_TRANSPARENT = (1 << 0),
- SCI_WINDOWMGR_STYLE_NOFRAME = (1 << 1),
- SCI_WINDOWMGR_STYLE_TITLE = (1 << 2),
- SCI_WINDOWMGR_STYLE_TOPMOST = (1 << 3),
- SCI_WINDOWMGR_STYLE_USER = (1 << 7)
-};
-
GfxPorts::GfxPorts(SegManager *segMan, GfxScreen *screen)
: _segMan(segMan), _screen(screen) {
}
diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h
index 453cb50986..21c6d31ebd 100644
--- a/engines/sci/graphics/ports.h
+++ b/engines/sci/graphics/ports.h
@@ -26,6 +26,7 @@
#ifndef SCI_GRAPHICS_PORTS_H
#define SCI_GRAPHICS_PORTS_H
+#include "common/serializer.h"
#include "common/list.h"
#include "common/array.h"
@@ -39,11 +40,20 @@ class GfxText16;
#define PORTS_FIRSTWINDOWID 2
#define PORTS_FIRSTSCRIPTWINDOWID 3
+// window styles
+enum {
+ SCI_WINDOWMGR_STYLE_TRANSPARENT = (1 << 0),
+ SCI_WINDOWMGR_STYLE_NOFRAME = (1 << 1),
+ SCI_WINDOWMGR_STYLE_TITLE = (1 << 2),
+ SCI_WINDOWMGR_STYLE_TOPMOST = (1 << 3),
+ SCI_WINDOWMGR_STYLE_USER = (1 << 7)
+};
+
/**
* Ports class, includes all port managment for SCI0->SCI1.1 games. Ports are some sort of windows in SCI
* this class also handles adjusting coordinates to a specific port
*/
-class GfxPorts {
+class GfxPorts : public Common::Serializable {
public:
GfxPorts(SegManager *segMan, GfxScreen *screen);
~GfxPorts();
@@ -103,6 +113,8 @@ public:
Common::Rect _menuLine;
Port *_curPort;
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
+
private:
typedef Common::List<Port *> PortList;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 6eabc7c9f0..5a96a3167f 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -122,9 +122,9 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
// Initialize the actual screen
- if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) {
- // For SCI1.1 Mac, we need to expand the screen to accommodate for
- // the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
+ if (g_sci->hasMacIconBar()) {
+ // For SCI1.1 Mac games with the custom icon bar, we need to expand the screen
+ // to accommodate for the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
if (g_sci->getGameId() == GID_KQ6)
initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320);
else if (g_sci->getGameId() == GID_QFG1VGA)
@@ -616,6 +616,11 @@ void GfxScreen::dither(bool addToFlag) {
}
}
+// Force a color combination into memorial
+void GfxScreen::ditherForceMemorial(byte color) {
+ _unditherMemorial[color] = 256;
+}
+
void GfxScreen::debugUnditherSetState(bool flag) {
_unditherState = flag;
}
@@ -649,20 +654,41 @@ void GfxScreen::debugShowMap(int mapNo) {
copyToScreen();
}
-void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight) {
+void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel) {
+ assert(bytesPerPixel == 1 || bytesPerPixel == 2);
const int newWidth = srcWidth * 2;
+ const int pitch = newWidth * bytesPerPixel;
const byte *srcPtr = src;
- for (int y = 0; y < srcHeight; y++) {
- for (int x = 0; x < srcWidth; x++) {
- const byte color = *srcPtr++;
- dst[0] = color;
- dst[1] = color;
- dst[newWidth] = color;
- dst[newWidth + 1] = color;
- dst += 2;
+ if (bytesPerPixel == 1) {
+ for (int y = 0; y < srcHeight; y++) {
+ for (int x = 0; x < srcWidth; x++) {
+ const byte color = *srcPtr++;
+ dst[0] = color;
+ dst[1] = color;
+ dst[newWidth] = color;
+ dst[newWidth + 1] = color;
+ dst += 2;
+ }
+ dst += newWidth;
+ }
+ } else if (bytesPerPixel == 2) {
+ for (int y = 0; y < srcHeight; y++) {
+ for (int x = 0; x < srcWidth; x++) {
+ const byte color = *srcPtr++;
+ const byte color2 = *srcPtr++;
+ dst[0] = color;
+ dst[1] = color2;
+ dst[2] = color;
+ dst[3] = color2;
+ dst[pitch] = color;
+ dst[pitch + 1] = color2;
+ dst[pitch + 2] = color;
+ dst[pitch + 3] = color2;
+ dst += 4;
+ }
+ dst += pitch;
}
- dst += newWidth;
}
}
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 97f5736289..dfc7a65311 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -109,12 +109,13 @@ public:
void getPalette(Palette *pal);
void setPalette(Palette *pal);
- void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight);
+ void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel = 1);
void adjustToUpscaledCoordinates(int16 &y, int16 &x);
void adjustBackUpscaledCoordinates(int16 &y, int16 &x);
void dither(bool addToFlag);
+ void ditherForceMemorial(byte color);
void debugUnditherSetState(bool flag);
int16 *unditherGetMemorial();
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 6b22ed397e..9513b11811 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -217,7 +217,7 @@ void GfxView::initData(GuiResourceId resourceId) {
case 0:
break; // don't do anything, we already have _isScaleable set
default:
- error("unsupported flags byte inside sci1.1 view");
+ error("unsupported flags byte (%d) inside sci1.1 view", _resourceData[3]);
break;
}
@@ -354,6 +354,16 @@ void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, C
outRect.top = outRect.bottom - celInfo->height;
}
+void GfxView::getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ int16 adjustY = y - celInfo->height + celInfo->displaceY + 1;
+ int16 adjustX = x - ((celInfo->width - 1) >> 1) + celInfo->displaceX;
+ outRect.top += adjustY;
+ outRect.bottom += adjustY;
+ outRect.left += adjustX;
+ outRect.right += adjustX;
+}
+
void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const {
int16 scaledDisplaceX, scaledDisplaceY;
int16 scaledWidth, scaledHeight;
@@ -525,6 +535,10 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
if (width <= 3)
return;
+ // We need at least 2 pixel lines
+ if (height < 2)
+ return;
+
// If EGA mapping is used for this view, dont do undithering as well
if (_EGAmapping)
return;
@@ -533,20 +547,28 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
byte *curPtr;
byte color1, color2;
+ byte nextColor1, nextColor2;
int16 y, x;
memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
// Count all seemingly dithered pixel-combinations as soon as at least 4
- // pixels are adjacent
+ // pixels are adjacent and check pixels in the following line as well to
+ // be the reverse pixel combination
+ int16 checkHeight = height - 1;
curPtr = bitmapPtr;
- for (y = 0; y < height; y++) {
+ byte *nextPtr = curPtr + width;
+ for (y = 0; y < checkHeight; y++) {
color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
+ nextColor1 = nextPtr[0] << 4; nextColor2 = (nextPtr[2] << 4) | nextPtr[1];
curPtr += 3;
+ nextPtr += 3;
for (x = 3; x < width; x++) {
color1 = (color1 << 4) | (color2 >> 4);
color2 = (color2 << 4) | *curPtr++;
- if (color1 == color2)
+ nextColor1 = (nextColor1 >> 4) | (nextColor2 << 4);
+ nextColor2 = (nextColor2 >> 4) | *nextPtr++ << 4;
+ if ((color1 == color2) && (color1 == nextColor1) && (color1 == nextColor2))
bitmapMemorial[color1]++;
}
}
@@ -583,9 +605,10 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
if (unditherTable[color]) {
// Some color with black? Turn colors around, otherwise it won't
// be the right color at all.
+ byte unditheredColor = color;
if ((color & 0xF0) == 0)
- color = (color << 4) | (color >> 4);
- curPtr[0] = color; curPtr[1] = color;
+ unditheredColor = (color << 4) | (color >> 4);
+ curPtr[0] = unditheredColor; curPtr[1] = unditheredColor;
}
curPtr++;
}
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 990a7e2f71..f785e3c475 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -66,6 +66,7 @@ public:
int16 getHeight(int16 loopNo, int16 celNo) const;
const CelInfo *getCelInfo(int16 loopNo, int16 celNo) const;
void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
+ void getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const;
const byte *getBitmap(int16 loopNo, int16 celNo);
void draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 344eef76d4..ad133e8a82 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -27,6 +27,7 @@ MODULE_OBJS := \
engine/kstring.o \
engine/kvideo.o \
engine/message.o \
+ engine/object.o \
engine/savegame.o \
engine/script.o \
engine/scriptdebug.o \
diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp
index 03e9d29660..8a6cd2dd4d 100644
--- a/engines/sci/parser/grammar.cpp
+++ b/engines/sci/parser/grammar.cpp
@@ -93,49 +93,49 @@ static void vocab_print_rule(ParseRule *rule) {
return;
}
- printf("[%03x] -> ", rule->_id);
+ debugN("[%03x] -> ", rule->_id);
if (rule->_data.empty())
- printf("e");
+ debugN("e");
for (uint i = 0; i < rule->_data.size(); i++) {
uint token = rule->_data[i];
if (token == TOKEN_OPAREN) {
if (i == rule->_firstSpecial)
- printf("_");
+ debugN("_");
- printf("(");
+ debugN("(");
wspace = 0;
} else if (token == TOKEN_CPAREN) {
if (i == rule->_firstSpecial)
- printf("_");
+ debugN("_");
- printf(")");
+ debugN(")");
wspace = 0;
} else {
if (wspace)
- printf(" ");
+ debugN(" ");
if (i == rule->_firstSpecial)
- printf("_");
+ debugN("_");
if (token & TOKEN_TERMINAL_CLASS)
- printf("C(%04x)", token & 0xffff);
+ debugN("C(%04x)", token & 0xffff);
else if (token & TOKEN_TERMINAL_GROUP)
- printf("G(%04x)", token & 0xffff);
+ debugN("G(%04x)", token & 0xffff);
else if (token & TOKEN_STUFFING_LEAF)
- printf("%03x", token & 0xffff);
+ debugN("%03x", token & 0xffff);
else if (token & TOKEN_STUFFING_WORD)
- printf("{%03x}", token & 0xffff);
+ debugN("{%03x}", token & 0xffff);
else
- printf("[%03x]", token); /* non-terminal */
+ debugN("[%03x]", token); /* non-terminal */
wspace = 1;
}
if (i == rule->_firstSpecial)
- printf("_");
+ debugN("_");
}
- printf(" [%d specials]", rule->_numSpecials);
+ debugN(" [%d specials]", rule->_numSpecials);
}
static ParseRule *_vdup(ParseRule *a) {
@@ -321,13 +321,13 @@ void ParseRuleList::print() const {
const ParseRuleList *list = this;
int pos = 0;
while (list) {
- printf("R%03d: ", pos);
+ debugN("R%03d: ", pos);
vocab_print_rule(list->rule);
- printf("\n");
+ debugN("\n");
list = list->next;
pos++;
}
- printf("%d rules total.\n", pos);
+ debugN("%d rules total.\n", pos);
}
static ParseRuleList *_vocab_split_rule_list(ParseRuleList *list) {
@@ -524,9 +524,9 @@ static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *
else
writepos = _vbpt_append_word(nodes, pos, writepos, token & 0xffff);
} else {
- printf("\nError in parser (grammar.cpp, _vbpt_write_subexpression()): Rule data broken in rule ");
+ warning("\nError in parser (grammar.cpp, _vbpt_write_subexpression()): Rule data broken in rule ");
vocab_print_rule(rule);
- printf(", at token position %d\n", *pos);
+ debugN(", at token position %d\n", *pos);
return rulepos;
}
}
diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp
index 7393874856..e9c6d9847f 100644
--- a/engines/sci/parser/said.cpp
+++ b/engines/sci/parser/said.cpp
@@ -39,7 +39,7 @@ namespace Sci {
#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
-#define scidprintf printf
+#define scidprintf debugN
#else
void print_nothing(...) { }
#define scidprintf print_nothing
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index f9989b22a8..b1f928cdd9 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -458,44 +458,44 @@ void Vocabulary::debugDecipherSaidBlock(const byte *addr) {
nextItem = *addr++;
if (nextItem != 0xff) {
if ((!first) && (nextItem != 0xf0))
- printf(" ");
+ debugN(" ");
first = false;
if (nextItem < 0xf0) {
nextItem = nextItem << 8 | *addr++;
- printf("%s{%03x}", getAnyWordFromGroup(nextItem), nextItem);
+ debugN("%s{%03x}", getAnyWordFromGroup(nextItem), nextItem);
nextItem = 0; // Make sure that group 0xff doesn't abort
} else switch (nextItem) {
case 0xf0:
- printf(",");
+ debugN(",");
break;
case 0xf1:
- printf("&");
+ debugN("&");
break;
case 0xf2:
- printf("/");
+ debugN("/");
break;
case 0xf3:
- printf("(");
+ debugN("(");
break;
case 0xf4:
- printf(")");
+ debugN(")");
break;
case 0xf5:
- printf("[");
+ debugN("[");
break;
case 0xf6:
- printf("]");
+ debugN("]");
break;
case 0xf7:
- printf("#");
+ debugN("#");
break;
case 0xf8:
- printf("<");
+ debugN("<");
break;
case 0xf9:
- printf(">");
+ debugN(">");
break;
case 0xff:
break;
@@ -611,48 +611,48 @@ void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) {
int i;
if (tree->type == kParseTreeLeafNode) {
- printf("vocab_dump_parse_tree: Error: consp is nil\n");
+ debugN("vocab_dump_parse_tree: Error: consp is nil\n");
return;
}
if (lbranch) {
if (lbranch->type == kParseTreeBranchNode) {
- printf("\n");
+ debugN("\n");
for (i = 0; i < blanks; i++)
- printf(" ");
- printf("(");
+ debugN(" ");
+ debugN("(");
_vocab_recursive_ptree_dump(lbranch, blanks + 1);
- printf(")\n");
+ debugN(")\n");
for (i = 0; i < blanks; i++)
- printf(" ");
+ debugN(" ");
} else
- printf("%x", lbranch->value);
- printf(" ");
- }/* else printf ("nil");*/
+ debugN("%x", lbranch->value);
+ debugN(" ");
+ }/* else debugN ("nil");*/
if (rbranch) {
if (rbranch->type == kParseTreeBranchNode)
_vocab_recursive_ptree_dump(rbranch, blanks);
else {
- printf("%x", rbranch->value);
+ debugN("%x", rbranch->value);
while (rbranch->right) {
rbranch = rbranch->right;
- printf("/%x", rbranch->value);
+ debugN("/%x", rbranch->value);
}
}
- }/* else printf("nil");*/
+ }/* else debugN("nil");*/
}
void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes) {
- printf("(setq %s \n'(", tree_name);
+ debugN("(setq %s \n'(", tree_name);
_vocab_recursive_ptree_dump(nodes, 1);
- printf("))\n");
+ debugN("))\n");
}
void Vocabulary::dumpParseTree() {
- printf("(setq parse-tree \n'(");
+ debugN("(setq parse-tree \n'(");
_vocab_recursive_ptree_dump(_parserNodes, 1);
- printf("))\n");
+ debugN("))\n");
}
void Vocabulary::synonymizeTokens(ResultWordListList &words) {
@@ -673,9 +673,15 @@ void Vocabulary::printParserNodes(int num) {
con->DebugPrintf(" Node %03x: ", i);
if (_parserNodes[i].type == kParseTreeLeafNode)
con->DebugPrintf("Leaf: %04x\n", _parserNodes[i].value);
- else
- con->DebugPrintf("Branch: ->%04x, ->%04x\n", _parserNodes[i].left,
- _parserNodes[i].right);
+ else {
+ // FIXME: Do we really want to print the *addresses*
+ // of the left & right child?
+ // Note that one or both may be zero pointers, so we can't just
+ // print their values.
+ con->DebugPrintf("Branch: ->%p, ->%p\n",
+ (const void *)_parserNodes[i].left,
+ (const void *)_parserNodes[i].right);
+ }
}
}
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index 620d50c09d..baf30a03d7 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -243,7 +243,7 @@ public:
ParseRuleList *buildGNF(bool verbose = false);
/**
- * Deciphers a said block and dumps its content via printf.
+ * Deciphers a said block and dumps its content via debugN.
* For debugging only.
* @param pos pointer to the data to dump
*/
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 9809f10576..a19e7bc893 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -114,16 +114,21 @@ static const char *s_resourceTypeNames[] = {
"patch", "bitmap", "palette", "cdaudio",
"audio", "sync", "message", "map", "heap",
"audio36", "sync36", "xlate", "robot", "vmd",
- "chunk", "macibin", "macibis", "macpict"
+ "chunk", "animation", "etc", "duck", "clut",
+ "tga", "zzz", "macibin", "macibis", "macpict"
};
+// Resource type suffixes. Note that the
+// suffic of SCI3 scripts has been changed from
+// scr to csc
static const char *s_resourceTypeSuffixes[] = {
"v56", "p56", "scr", "tex", "snd",
"", "voc", "fon", "cur", "pat",
"bit", "pal", "cda", "aud", "syn",
"msg", "map", "hep", "", "",
"trn", "rbt", "vmd", "chk", "",
- "", ""
+ "etc", "duk", "clu", "tga", "zzz",
+ "", "", ""
};
const char *getResourceTypeName(ResourceType restype) {
@@ -144,18 +149,19 @@ static const ResourceType s_resTypeMapSci0[] = {
// TODO: 12 should be "Wave", but SCI seems to just store it in Audio resources
static const ResourceType s_resTypeMapSci21[] = {
- kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeText, // 0x00-0x03
- kResourceTypeSound, kResourceTypeMemory, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07
+ kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeAnimation, // 0x00-0x03
+ kResourceTypeSound, kResourceTypeEtc, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07
kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B
kResourceTypeInvalid, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F
kResourceTypeMap, kResourceTypeHeap, kResourceTypeChunk, kResourceTypeAudio36, // 0x10-0x13
- kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD // 0x14-0x17
+ kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD, // 0x14-0x17
+ kResourceTypeDuck, kResourceTypeClut, kResourceTypeTGA, kResourceTypeZZZ // 0x18-0x1B
};
ResourceType ResourceManager::convertResType(byte type) {
type &= 0x7f;
- if (_mapVersion != kResVersionSci32) {
+ if (_mapVersion < kResVersionSci2) {
// SCI0 - SCI2
if (type < ARRAYSIZE(s_resTypeMapSci0))
return s_resTypeMapSci0[type];
@@ -587,6 +593,8 @@ int ResourceManager::addAppropriateSources() {
int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
ResourceSource *map = 0;
+ Common::Array<ResourceSource *> sci21Maps;
+
#ifdef ENABLE_SCI32
ResourceSource *sci21PatchMap = 0;
const Common::FSNode *sci21PatchRes = 0;
@@ -605,8 +613,13 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
if (filename.contains("resmap.0")) {
const char *dot = strrchr(file->getName().c_str(), '.');
- int number = atoi(dot + 1);
- map = addExternalMap(file, number);
+ uint number = atoi(dot + 1);
+
+ // We need to store each of these maps for use later on
+ if (number >= sci21Maps.size())
+ sci21Maps.resize(number + 1);
+
+ sci21Maps[number] = addExternalMap(file, number);
}
#ifdef ENABLE_SCI32
@@ -619,7 +632,7 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
#endif
}
- if (!map)
+ if (!map && sci21Maps.empty())
return 0;
#ifdef ENABLE_SCI32
@@ -635,11 +648,17 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
Common::String filename = file->getName();
filename.toLowercase();
- if (filename.contains("resource.0") || filename.contains("ressci.0")) {
+ if (filename.contains("resource.0")) {
const char *dot = strrchr(filename.c_str(), '.');
int number = atoi(dot + 1);
addSource(new VolumeResourceSource(file->getName(), map, number, file));
+ } else if (filename.contains("ressci.0")) {
+ const char *dot = strrchr(filename.c_str(), '.');
+ int number = atoi(dot + 1);
+
+ // Match this volume to its own map
+ addSource(new VolumeResourceSource(file->getName(), sci21Maps[number], number, file));
}
}
@@ -660,11 +679,29 @@ int ResourceManager::addInternalSources() {
addSource(new AudioVolumeResourceSource(this, "RESOURCE.SFX", src, 0));
else if (Common::File::exists("RESOURCE.AUD"))
addSource(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0));
+ else
+ return 0;
++itr;
}
delete resources;
+
+#ifdef ENABLE_SCI32
+ if (_mapVersion >= kResVersionSci2) {
+ // If we have no scripts, but chunk 0 is present, open up the chunk
+ // to try to get to any scripts in there. The Lighthouse SCI2.1 demo
+ // does exactly this.
+
+ resources = listResources(kResourceTypeScript);
+
+ if (resources->empty() && testResource(ResourceId(kResourceTypeChunk, 0)))
+ addResourcesFromChunk(0);
+
+ delete resources;
+ }
+#endif
+
return 1;
}
@@ -779,7 +816,7 @@ void ChunkResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
}
void ResourceManager::addResourcesFromChunk(uint16 id) {
- addSource(new ChunkResourceSource(Common::String::printf("Chunk %d", id), id));
+ addSource(new ChunkResourceSource(Common::String::format("Chunk %d", id), id));
scanNewSources();
}
@@ -795,7 +832,7 @@ void ResourceManager::freeResourceSources() {
ResourceManager::ResourceManager() {
}
-void ResourceManager::init() {
+void ResourceManager::init(bool initFromFallbackDetector) {
_memoryLocked = 0;
_memoryLRU = 0;
_LRU.clear();
@@ -806,6 +843,13 @@ void ResourceManager::init() {
_mapVersion = detectMapVersion();
_volVersion = detectVolVersion();
+
+ // TODO/FIXME: Remove once SCI3 resource detection is finished
+ if ((_mapVersion == kResVersionSci3 || _volVersion == kResVersionSci3) && (_mapVersion != _volVersion)) {
+ warning("FIXME: Incomplete SCI3 detection: setting map and volume version to SCI3");
+ _mapVersion = _volVersion = kResVersionSci3;
+ }
+
if ((_volVersion == kResVersionUnknown) && (_mapVersion != kResVersionUnknown)) {
warning("Volume version not detected, but map version has been detected. Setting volume version to map version");
_volVersion = _mapVersion;
@@ -826,8 +870,13 @@ void ResourceManager::init() {
}
scanNewSources();
- addInternalSources();
- scanNewSources();
+
+ if (!initFromFallbackDetector) {
+ if (!addInternalSources())
+ error("Somehow I can't seem to find the sound files I need (RESOURCE.AUD/RESOURCE.SFX), aborting");
+
+ scanNewSources();
+ }
detectSciVersion();
@@ -858,21 +907,6 @@ void ResourceManager::init() {
}
#endif
}
-
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1) {
- // If we have no scripts, but chunk 0 is present, open up the chunk
- // to try to get to any scripts in there. The Lighthouse SCI2.1 demo
- // does exactly this.
-
- Common::List<ResourceId> *scriptList = listResources(kResourceTypeScript);
-
- if (scriptList->empty() && testResource(ResourceId(kResourceTypeChunk, 0)))
- addResourcesFromChunk(0);
-
- delete scriptList;
- }
-#endif
}
ResourceManager::~ResourceManager() {
@@ -940,7 +974,7 @@ void ResourceManager::freeOldResources() {
removeFromLRU(goner);
goner->unalloc();
#ifdef SCI_VERBOSE_RESMAN
- printf("resMan-debug: LRU: Freeing %s.%03d (%d bytes)\n", getResourceTypeName(goner->type), goner->number, goner->size);
+ debug("resMan-debug: LRU: Freeing %s.%03d (%d bytes)", getResourceTypeName(goner->type), goner->number, goner->size);
#endif
}
}
@@ -1024,8 +1058,10 @@ const char *ResourceManager::versionDescription(ResVersion version) const {
return "SCI1.1";
case kResVersionSci11Mac:
return "Mac SCI1.1+";
- case kResVersionSci32:
- return "SCI32";
+ case kResVersionSci2:
+ return "SCI2/2.1";
+ case kResVersionSci3:
+ return "SCI3";
}
return "Version not valid";
@@ -1036,6 +1072,8 @@ ResVersion ResourceManager::detectMapVersion() {
byte buff[6];
ResourceSource *rsrc= 0;
+ // TODO: Add SCI3 support
+
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
rsrc = *it;
@@ -1084,8 +1122,8 @@ ResVersion ResourceManager::detectMapVersion() {
directoryOffset = fileStream->readUint16LE();
// Only SCI32 has directory type < 0x80
- if (directoryType < 0x80 && (mapDetected == kResVersionUnknown || mapDetected == kResVersionSci32))
- mapDetected = kResVersionSci32;
+ if (directoryType < 0x80 && (mapDetected == kResVersionUnknown || mapDetected == kResVersionSci2))
+ mapDetected = kResVersionSci2;
else if (directoryType < 0x80 || ((directoryType & 0x7f) > 0x20 && directoryType != 0xFF))
break;
@@ -1127,7 +1165,7 @@ ResVersion ResourceManager::detectVolVersion() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
rsrc = *it;
-
+
if (rsrc->getSourceType() == kSourceVolume) {
if (rsrc->_resourceFile) {
fileStream = rsrc->_resourceFile->createReadStream();
@@ -1163,23 +1201,38 @@ ResVersion ResourceManager::detectVolVersion() {
bool failed = false;
bool sci11Align = false;
- // Check for SCI0, SCI1, SCI1.1 and SCI32 v2 (Gabriel Knight 1 CD) formats
+ // Check for SCI0, SCI1, SCI1.1, SCI32 v2 (Gabriel Knight 1 CD) and SCI32 v3 (LSL7) formats
while (!fileStream->eos() && fileStream->pos() < 0x100000) {
if (curVersion > kResVersionSci0Sci1Early)
fileStream->readByte();
resId = fileStream->readUint16LE();
- dwPacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE();
- dwUnpacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE();
+ dwPacked = (curVersion < kResVersionSci2) ? fileStream->readUint16LE() : fileStream->readUint32LE();
+ dwUnpacked = (curVersion < kResVersionSci2) ? fileStream->readUint16LE() : fileStream->readUint32LE();
+
+ // The compression field is present, but bogus when
+ // loading SCI3 volumes, the format is otherwise
+ // identical to SCI2. We therefore get the compression
+ // indicator here, but disregard it in the following
+ // code.
wCompression = fileStream->readUint16LE();
+
if (fileStream->eos()) {
delete fileStream;
return curVersion;
}
- int chk = (curVersion == kResVersionSci0Sci1Early) ? 4 : 20;
+ int chk;
+
+ if (curVersion == kResVersionSci0Sci1Early)
+ chk = 4;
+ else if (curVersion < kResVersionSci2)
+ chk = 20;
+ else
+ chk = 32; // We don't need this, but include it for completeness
+
int offs = curVersion < kResVersionSci11 ? 4 : 0;
- if ((curVersion < kResVersionSci32 && wCompression > chk)
- || (curVersion == kResVersionSci32 && wCompression != 0 && wCompression != 32)
+ if ((curVersion < kResVersionSci2 && wCompression > chk)
+ || (curVersion == kResVersionSci2 && wCompression != 0 && wCompression != 32)
|| (wCompression == 0 && dwPacked != dwUnpacked + offs)
|| (dwUnpacked < dwPacked - offs)) {
@@ -1192,7 +1245,9 @@ ResVersion ResourceManager::detectVolVersion() {
// Later versions (e.g. QFG1VGA) have resources word-aligned
sci11Align = true;
} else if (curVersion == kResVersionSci11) {
- curVersion = kResVersionSci32;
+ curVersion = kResVersionSci2;
+ } else if (curVersion == kResVersionSci2) {
+ curVersion = kResVersionSci3;
} else {
// All version checks failed, exit loop
failed = true;
@@ -1207,7 +1262,7 @@ ResVersion ResourceManager::detectVolVersion() {
fileStream->seek(dwPacked - 4, SEEK_CUR);
else if (curVersion == kResVersionSci11)
fileStream->seek(sci11Align && ((9 + dwPacked) % 2) ? dwPacked + 1 : dwPacked, SEEK_CUR);
- else if (curVersion == kResVersionSci32)
+ else if (curVersion >= kResVersionSci2)
fileStream->seek(dwPacked, SEEK_CUR);
}
@@ -1390,7 +1445,7 @@ void ResourceManager::readResourcePatches() {
for (int i = kResourceTypeView; i < kResourceTypeInvalid; ++i) {
// Ignore the types that can't be patched (and Robot/VMD is handled externally for now)
- if (!s_resourceTypeSuffixes[i] || i == kResourceTypeRobot || i == kResourceTypeVMD)
+ if (!s_resourceTypeSuffixes[i] || (i >= kResourceTypeRobot && i != kResourceTypeChunk))
continue;
files.clear();
@@ -1404,6 +1459,12 @@ void ResourceManager::readResourcePatches() {
mask += s_resourceTypeSuffixes[i];
SearchMan.listMatchingMembers(files, mask);
+ if (i == kResourceTypeScript && files.size() == 0) {
+ // SCI3 (we can't use getSciVersion() at this point)
+ mask = "*.csc";
+ SearchMan.listMatchingMembers(files, mask);
+ }
+
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
bool bAdd = false;
name = (*x)->getName();
@@ -1557,10 +1618,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// the actual resource file.
int mapVolumeNr = volume_nr + map->_volumeNumber;
ResourceSource *source = findVolume(map, mapVolumeNr);
- // FIXME: this code has serious issues with multiple RESMAP.* files (like in unmodified gk2)
- // adding a resource with source == NULL would crash later on
- if (!source)
- error("Unable to find volume for map %s volumeNr %d", map->getLocationName().c_str(), mapVolumeNr);
+
+ assert(source);
Resource *resource = _resMap.getVal(resId, NULL);
if (!resource) {
@@ -1583,10 +1642,12 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
return 0;
}
-struct {
+struct MacResTag {
uint32 tag;
ResourceType type;
-} static const macResTagMap[] = {
+};
+
+static const MacResTag macResTagMap[] = {
{ MKID_BE('V56 '), kResourceTypeView },
{ MKID_BE('P56 '), kResourceTypePic },
{ MKID_BE('SCR '), kResourceTypeScript },
@@ -1731,12 +1792,22 @@ int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream
wCompression = 0;
break;
#ifdef ENABLE_SCI32
- case kResVersionSci32:
+ case kResVersionSci2:
+ case kResVersionSci3:
type = _resMan->convertResType(file->readByte());
number = file->readUint16LE();
szPacked = file->readUint32LE();
szUnpacked = file->readUint32LE();
+
+ // The same comment applies here as in
+ // detectVolVersion regarding SCI3. We ignore the
+ // compression field for SCI3 games, but must presume
+ // it exists in the file.
wCompression = file->readUint16LE();
+
+ if (volVersion == kResVersionSci3)
+ wCompression = szPacked != szUnpacked ? 32 : 0;
+
break;
#endif
default:
@@ -1953,7 +2024,7 @@ void ResourceManager::detectSciVersion() {
#ifdef ENABLE_SCI32
viewCompression = getViewCompression();
#else
- if (_volVersion == kResVersionSci32) {
+ if (_volVersion >= kResVersionSci2) {
// SCI32 support isn't built in, thus view detection will fail
viewCompression = kCompUnknown;
} else {
@@ -1975,7 +2046,7 @@ void ResourceManager::detectSciVersion() {
|| _volVersion == kResVersionSci11Mac
#ifdef ENABLE_SCI32
|| viewCompression == kCompSTACpack
- || _volVersion == kResVersionSci32 // kq7
+ || _volVersion == kResVersionSci2 // kq7
#endif
) {
// SCI1.1 VGA views
@@ -1985,7 +2056,7 @@ void ResourceManager::detectSciVersion() {
// Otherwise we detect it from a view
_viewType = detectViewType();
#else
- if (_volVersion == kResVersionSci32 && viewCompression == kCompUnknown) {
+ if (_volVersion == kResVersionSci2 && viewCompression == kCompUnknown) {
// A SCI32 game, but SCI32 support is disabled. Force the view type
// to kViewVga11, as we can't read from the game's resource files
_viewType = kViewVga11;
@@ -2009,17 +2080,20 @@ void ResourceManager::detectSciVersion() {
}
// Handle SCI32 versions here
- if (_volVersion == kResVersionSci32) {
+ if (_volVersion >= kResVersionSci2) {
+ Common::List<ResourceId> *heaps = listResources(kResourceTypeHeap);
// SCI2.1/3 and SCI1 Late resource maps are the same, except that
// SCI1 Late resource maps have the resource types or'd with
// 0x80. We differentiate between SCI2 and SCI2.1/3 based on that.
- // TODO: Differentiate between SCI2.1 and SCI3
if (_mapVersion == kResVersionSci1Late) {
s_sciVersion = SCI_VERSION_2;
return;
- } else {
+ } else if (!heaps->empty()) {
s_sciVersion = SCI_VERSION_2_1;
return;
+ } else {
+ s_sciVersion = SCI_VERSION_3;
+ return;
}
}
@@ -2284,7 +2358,7 @@ bool ResourceManager::hasSci1Voc900() {
return offset == res->size;
}
-// Same function as Script::findBlock(). Slight code
+// Same function as Script::findBlockSCI0(). Slight code
// duplication here, but this has been done to keep the resource
// manager independent from the rest of the engine
static byte *findSci0ExportsBlock(byte *buffer) {
@@ -2310,6 +2384,24 @@ static byte *findSci0ExportsBlock(byte *buffer) {
return NULL;
}
+// This code duplicates Script::relocateOffsetSci3, but we can't use
+// that here since we can't instantiate scripts at this point.
+static int relocateOffsetSci3(const byte *buf, uint32 offset) {
+ int relocStart = READ_LE_UINT32(buf + 8);
+ int relocCount = READ_LE_UINT16(buf + 18);
+ const byte *seeker = buf + relocStart;
+
+ for (int i = 0; i < relocCount; ++i) {
+ if (READ_SCI11ENDIAN_UINT32(seeker) == offset) {
+ // TODO: Find out what UINT16 at (seeker + 8) means
+ return READ_SCI11ENDIAN_UINT16(buf + offset) + READ_SCI11ENDIAN_UINT32(seeker + 4);
+ }
+ seeker += 10;
+ }
+
+ return -1;
+}
+
reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false);
@@ -2318,7 +2410,7 @@ reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
byte *offsetPtr = 0;
- if (getSciVersion() < SCI_VERSION_1_1) {
+ if (getSciVersion() <= SCI_VERSION_1_LATE) {
byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data;
// Check if the first block is the exports block (in most cases, it is)
@@ -2331,35 +2423,42 @@ reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
error("Unable to find exports block from script 0");
offsetPtr += 4 + 2;
}
- } else {
+
+ int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
+ return make_reg(1, offset);
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
offsetPtr = script->data + 4 + 2 + 2;
- }
-
- int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
- // In SCI1.1 and newer, the heap is appended at the end of the script,
- // so adjust the offset accordingly
- if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) {
- offset += script->size;
+ // In SCI1.1 - SCI2.1, the heap is appended at the end of the script,
+ // so adjust the offset accordingly if requested
+ int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
+ if (addSci11ScriptOffset) {
+ offset += script->size;
- // Ensure that the start of the heap is word-aligned - same as in Script::init()
- if (script->size & 2)
- offset++;
- }
+ // Ensure that the start of the heap is word-aligned - same as in Script::init()
+ if (script->size & 2)
+ offset++;
+ }
- return make_reg(1, offset);
+ return make_reg(1, offset);
+ } else {
+ return make_reg(1, relocateOffsetSci3(script->data, 22));
+ }
}
Common::String ResourceManager::findSierraGameId() {
- // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated
+ // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1 - SCI2.1,
+ // it's in a separate heap resource
Resource *heap = 0;
int nameSelector = 3;
if (getSciVersion() < SCI_VERSION_1_1) {
heap = findResource(ResourceId(kResourceTypeScript, 0), false);
- } else {
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
heap = findResource(ResourceId(kResourceTypeHeap, 0), false);
nameSelector += 5;
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ warning("TODO: findSierraGameId(): SCI3 equivalent");
}
if (!heap)
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index f5d6517398..ad6a7cac3e 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -96,9 +96,19 @@ enum ResourceType {
kResourceTypeAudio36,
kResourceTypeSync36,
kResourceTypeTranslation, // Currently unsupported
+
+ // SCI2.1+ Resources
kResourceTypeRobot,
kResourceTypeVMD,
kResourceTypeChunk,
+ kResourceTypeAnimation,
+
+ // SCI3 Resources
+ kResourceTypeEtc,
+ kResourceTypeDuck,
+ kResourceTypeClut,
+ kResourceTypeTGA,
+ kResourceTypeZZZ,
// Mac-only resources
kResourceTypeMacIconBarPictN, // IBIN resources (icon bar, not selected)
@@ -117,7 +127,8 @@ enum ResVersion {
kResVersionSci1Late,
kResVersionSci11,
kResVersionSci11Mac,
- kResVersionSci32
+ kResVersionSci2,
+ kResVersionSci3
};
class ResourceManager;
@@ -272,7 +283,7 @@ public:
/**
* Initializes the resource manager.
*/
- void init();
+ void init(bool initFromFallbackDetector = false);
int addAppropriateSources();
int addAppropriateSources(const Common::FSList &fslist); // TODO: Switch from FSList to Common::Archive?
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index e6b8fd06c2..9949373852 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -276,7 +276,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
#ifndef ENABLE_SCI32
// SCI32 support is not built in. Check if this is a SCI32 game
// and if it is abort here.
- if (_volVersion == kResVersionSci32)
+ if (_volVersion >= kResVersionSci2)
return SCI_ERROR_RESMAP_NOT_FOUND;
#endif
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 3fe398f426..43422b4ede 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -39,6 +39,7 @@
#include "sci/engine/features.h"
#include "sci/engine/message.h"
+#include "sci/engine/object.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/script.h" // for script_adjust_opcode_formats
@@ -128,9 +129,10 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions
SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot movie files
SearchMan.addSubDirectoryMatching(gameDataDir, "robots"); // robot movie files
- SearchMan.addSubDirectoryMatching(gameDataDir, "movie"); // vmd movie files
- SearchMan.addSubDirectoryMatching(gameDataDir, "movies"); // vmd movie files
- SearchMan.addSubDirectoryMatching(gameDataDir, "vmd"); // vmd movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "movie"); // VMD movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "movies"); // VMD movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "vmd"); // VMD movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "duk"); // Duck movie files in Phantasmagoria 2
// Add the patches directory, except for KQ6CD; The patches folder in some versions of KQ6CD
// is for the demo of Phantasmagoria, included in the disk
@@ -180,9 +182,9 @@ Common::Error SciEngine::run() {
g_eventRec.registerRandomSource(_rng, "sci");
// Assign default values to the config manager, in case settings are missing
- ConfMan.registerDefault("sci_undither", "true");
ConfMan.registerDefault("sci_originalsaveload", "false");
ConfMan.registerDefault("native_fb01", "false");
+ ConfMan.registerDefault("windows_cursors", "false"); // Windows cursors for KQ6 Windows
_resMan = new ResourceManager();
assert(_resMan);
@@ -210,7 +212,7 @@ Common::Error SciEngine::run() {
// Initialize the game screen
_gfxScreen = new GfxScreen(_resMan);
- _gfxScreen->debugUnditherSetState(ConfMan.getBool("sci_undither"));
+ _gfxScreen->debugUnditherSetState(ConfMan.getBool("disable_dithering"));
// Create debugger console. It requires GFX to be initialized
_console = new Console(this);
@@ -303,10 +305,10 @@ Common::Error SciEngine::run() {
if (buggyScript && (buggyScript->size == 12354 || buggyScript->size == 12362)) {
showScummVMDialog("A known buggy game script has been detected, which could "
- "prevent you from progressing later on in the game, during "
- "the sequence with the Green Man's riddles. Please, apply "
- "the latest patch for this game by Sierra to avoid possible "
- "problems");
+ "prevent you from progressing later on in the game, during "
+ "the sequence with the Green Man's riddles. Please, apply "
+ "the latest patch for this game by Sierra to avoid possible "
+ "problems");
}
}
@@ -325,16 +327,16 @@ Common::Error SciEngine::run() {
case GID_SQ4:
case GID_FAIRYTALES:
showScummVMDialog("You have selected General MIDI as a sound device. Sierra "
- "has provided after-market support for General MIDI for this "
- "game in their \"General MIDI Utility\". Please, apply this "
- "patch in order to enjoy MIDI music with this game. Once you "
- "have obtained it, you can unpack all of the included *.PAT "
- "files in your ScummVM extras folder and ScummVM will add the "
- "appropriate patch automatically. Alternatively, you can follow "
- "the instructions in the READ.ME file included in the patch and "
- "rename the associated *.PAT file to 4.PAT and place it in the "
- "game folder. Without this patch, General MIDI music for this "
- "game will sound badly distorted.");
+ "has provided after-market support for General MIDI for this "
+ "game in their \"General MIDI Utility\". Please, apply this "
+ "patch in order to enjoy MIDI music with this game. Once you "
+ "have obtained it, you can unpack all of the included *.PAT "
+ "files in your ScummVM extras folder and ScummVM will add the "
+ "appropriate patch automatically. Alternatively, you can follow "
+ "the instructions in the READ.ME file included in the patch and "
+ "rename the associated *.PAT file to 4.PAT and place it in the "
+ "game folder. Without this patch, General MIDI music for this "
+ "game will sound badly distorted.");
break;
default:
break;
@@ -342,6 +344,13 @@ Common::Error SciEngine::run() {
}
}
+ if (gameHasFanMadePatch()) {
+ showScummVMDialog("Your game is patched with a fan made script patch. Such patches have "
+ "been reported to cause issues, as they modify game scripts extensively. "
+ "The issues that these patches fix do not occur in ScummVM, so you are "
+ "advised to remove this patch from your game folder in order to avoid "
+ "having unexpected errors and/or issues later on.");
+ }
runGame();
@@ -350,6 +359,69 @@ Common::Error SciEngine::run() {
return Common::kNoError;
}
+bool SciEngine::gameHasFanMadePatch() {
+ struct FanMadePatchInfo {
+ SciGameId gameID;
+ uint16 targetScript;
+ uint16 targetSize;
+ uint16 patchedByteOffset;
+ byte patchedByte;
+ };
+
+ const FanMadePatchInfo patchInfo[] = {
+ // game script size offset byte
+ // ** NRS Patches **************************
+ { GID_HOYLE3, 994, 2580, 656, 0x78 },
+ { GID_KQ1, 85, 5156, 631, 0x02 },
+ { GID_LAURABOW2, 994, 4382, 0, 0x00 },
+ { GID_LONGBOW, 994, 4950, 1455, 0x78 }, // English
+ { GID_LONGBOW, 994, 5020, 1469, 0x78 }, // German
+ { GID_LSL1, 803, 592, 342, 0x01 },
+ { GID_LSL3, 380, 6148, 195, 0x35 },
+ { GID_LSL5, 994, 4810, 1342, 0x78 }, // English
+ { GID_LSL5, 994, 4942, 1392, 0x76 }, // German
+ { GID_PQ1, 994, 4332, 1473, 0x78 },
+ { GID_PQ2, 200, 10614, 0, 0x00 },
+ { GID_PQ3, 994, 4686, 1291, 0x78 }, // English
+ { GID_PQ3, 994, 4734, 1283, 0x78 }, // German
+ { GID_QFG1VGA, 994, 4388, 0, 0x00 },
+ { GID_QFG3, 994, 4714, 0, 0x00 },
+ // TODO: Disabled, as it fixes a whole lot of bugs which can't be tested till SCI2.1 support is finished
+ //{ GID_QFG4, 710, 11477, 0, 0x00 },
+ { GID_SQ1, 994, 4740, 0, 0x00 },
+ { GID_SQ5, 994, 4142, 1496, 0x78 }, // English/German/French
+ // TODO: Disabled, till we can test the Italian version
+ //{ GID_SQ5, 994, 4148, 0, 0x00 }, // Italian - patched file is the same size as the original
+ // TODO: The bugs in SQ6 can't be tested till SCI2.1 support is finished
+ //{ GID_SQ6, 380, 16308, 15042, 0x0C }, // English
+ //{ GID_SQ6, 380, 11652, 0, 0x00 }, // German - patched file is the same size as the original
+ // ** End marker ***************************
+ { GID_FANMADE, 0, 0, 0, 0x00 }
+ };
+
+ int curEntry = 0;
+
+ while (true) {
+ if (patchInfo[curEntry].targetSize == 0)
+ break;
+
+ if (patchInfo[curEntry].gameID == getGameId()) {
+ Resource *targetScript = _resMan->findResource(ResourceId(kResourceTypeScript, patchInfo[curEntry].targetScript), 0);
+
+ if (targetScript && targetScript->size + 2 == patchInfo[curEntry].targetSize) {
+ if (patchInfo[curEntry].patchedByteOffset == 0)
+ return true;
+ else if (targetScript->data[patchInfo[curEntry].patchedByteOffset - 2] == patchInfo[curEntry].patchedByte)
+ return true;
+ }
+ }
+
+ curEntry++;
+ }
+
+ return false;
+}
+
static byte patchGameRestoreSave[] = {
0x39, 0x03, // pushi 03
0x76, // push0
@@ -363,6 +435,8 @@ void SciEngine::patchGameSaveRestore(SegManager *segMan) {
const Object *gameObject = segMan->getObject(_gameObjectAddress);
const uint16 gameMethodCount = gameObject->getMethodCount();
const Object *gameSuperObject = segMan->getObject(_gameSuperClassAddress);
+ if (!gameSuperObject)
+ gameSuperObject = gameObject; // happens in KQ5CD, when loading saved games before r54510
const uint16 gameSuperMethodCount = gameSuperObject->getMethodCount();
reg_t methodAddress;
const uint16 kernelCount = _kernel->getKernelNamesSize();
@@ -484,7 +558,7 @@ bool SciEngine::initGame() {
_vocabulary->reset();
}
- _gamestate->gameStartTime = _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
+ _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
// Load game language into printLang property of game object
setSciLanguage();
@@ -514,7 +588,7 @@ void SciEngine::initGraphics() {
_gfxPaint32 = 0;
#endif
- if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1)
+ if (hasMacIconBar())
_gfxMacIconBar = new GfxMacIconBar();
bool paletteMerging = true;
@@ -580,6 +654,8 @@ void SciEngine::initStackBaseWithSelector(Selector selector) {
}
void SciEngine::runGame() {
+ setTotalPlayTime(0);
+
initStackBaseWithSelector(SELECTOR(play)); // Call the play selector
// Attach the debug console on game startup, if requested
@@ -633,8 +709,10 @@ void SciEngine::exitGame() {
GUI::Debugger *SciEngine::getDebugger() {
if (_gamestate) {
ExecStack *xs = &(_gamestate->_executionStack.back());
- xs->addr.pc.offset = _debugState.old_pc_offset;
- xs->sp = _debugState.old_sp;
+ if (xs) {
+ xs->addr.pc.offset = _debugState.old_pc_offset;
+ xs->sp = _debugState.old_sp;
+ }
}
_debugState.runningStep = 0; // Stop multiple execution
@@ -664,8 +742,16 @@ bool SciEngine::isDemo() const {
return _gameDescription->flags & ADGF_DEMO;
}
+bool SciEngine::isCD() const {
+ return _gameDescription->flags & ADGF_CD;
+}
+
+bool SciEngine::hasMacIconBar() const {
+ return _resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1 && getGameId() != GID_HOYLE4;
+}
+
Common::String SciEngine::getSavegameName(int nr) const {
- return _targetName + Common::String::printf(".%03d", nr);
+ return _targetName + Common::String::format(".%03d", nr);
}
Common::String SciEngine::getSavegamePattern() const {
@@ -702,6 +788,8 @@ int SciEngine::inQfGImportRoom() const {
void SciEngine::pauseEngineIntern(bool pause) {
_mixer->pauseAll(pause);
+ if (_soundCmd)
+ _soundCmd->pauseAll(pause);
}
void SciEngine::syncSoundSettings() {
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 7239abad17..716419d103 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -39,8 +39,11 @@ struct ADGameDescription;
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Newer Sierra adventure games (based on FreeSCI)
+ *
+ * @todo give a concrete list of supported games. Could also
+ * list future games, with status for each.
*/
namespace Sci {
@@ -163,7 +166,7 @@ enum SciGameId {
GID_QFG4,
GID_RAMA,
GID_SHIVERS,
- GID_SHIVERS2,
+ //GID_SHIVERS2, // Not SCI
GID_SLATER,
GID_SQ1,
GID_SQ3,
@@ -228,6 +231,8 @@ public:
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
bool isDemo() const;
+ bool isCD() const;
+ bool hasMacIconBar() const;
inline ResourceManager *getResMan() const { return _resMan; }
inline Kernel *getKernel() const { return _kernel; }
@@ -344,6 +349,8 @@ private:
void initStackBaseWithSelector(Selector selector);
+ bool gameHasFanMadePatch();
+
const ADGameDescription *_gameDescription;
const SciGameId _gameId;
ResourceManager *_resMan; /**< The resource manager */
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 640273c20e..829a94d90c 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -29,7 +29,10 @@
#include "sci/engine/seg_manager.h"
#include "sci/sound/audio.h"
+#include "backends/audiocd/audiocd.h"
+
#include "common/file.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "sound/audiostream.h"
@@ -279,7 +282,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
// additional buffer here. MP3/OGG/FLAC decompression works on-the-fly
// instead.
memcpy(compressedData, audioRes->data, audioRes->size);
- Common::MemoryReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size, DisposeAfterUse::YES);
+ Common::SeekableReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size, DisposeAfterUse::YES);
switch (audioCompressionType) {
case MKID_BE('MP3 '):
@@ -313,7 +316,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
}
} else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKID_BE('RIFF')) {
// WAVE detected
- Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+ Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
// Calculate samplelen from WAVE header
int waveSize = 0, waveRate = 0;
@@ -325,7 +328,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES);
} else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKID_BE('FORM')) {
// AIFF detected
- Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+ Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
// Calculate samplelen from AIFF header
int waveSize = 0, waveRate = 0;
@@ -338,7 +341,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
} else if (audioRes->size > 14 && READ_BE_UINT16(audioRes->data) == 1 && READ_BE_UINT16(audioRes->data + 2) == 1
&& READ_BE_UINT16(audioRes->data + 4) == 5 && READ_BE_UINT32(audioRes->data + 10) == 0x00018051) {
// Mac snd detected
- Common::MemoryReadStream *sndStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+ Common::SeekableReadStream *sndStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
audioSeekStream = Audio::makeMacSndStream(sndStream, DisposeAfterUse::YES);
} else {
diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp
index 4b591eb609..7cf798a27e 100644
--- a/engines/sci/sound/drivers/amigamac.cpp
+++ b/engines/sci/sound/drivers/amigamac.cpp
@@ -29,6 +29,7 @@
#include "common/file.h"
#include "common/frac.h"
+#include "common/memstream.h"
#include "common/util.h"
namespace Sci {
@@ -288,7 +289,7 @@ void MidiDriver_AmigaMac::playInstrument(int16 *dest, Voice *channel, int count)
void MidiDriver_AmigaMac::changeInstrument(int channel, int instrument) {
#ifdef DEBUG
if (_bank.instruments[instrument][0])
- printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument);
+ debugN("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument);
else
warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel);
#endif
@@ -488,13 +489,13 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C
instrument->name[29] = 0;
#ifdef DEBUG
- printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
+ debugN("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
*id, instrument->name, size);
- printf(" Mode: %02x\n", instrument->mode);
- printf(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off");
- printf(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off");
- printf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
- printf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
+ debugN(" Mode: %02x\n", instrument->mode);
+ debugN(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off");
+ debugN(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off");
+ debugN(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
+ debugN(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
#endif
instrument->samples = (int8 *) malloc(size + 1);
@@ -745,7 +746,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) {
strncpy(_bank.name, (char *) header + 8, 29);
_bank.name[29] = 0;
#ifdef DEBUG
- printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+ debugN("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
#endif
for (uint i = 0; i < _bank.size; i++) {
@@ -784,7 +785,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &fil
strncpy(_bank.name, (char *) header + 8, 29);
_bank.name[29] = 0;
#ifdef DEBUG
- printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
+ debugN("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
#endif
Common::Array<uint32> instrumentOffsets;
diff --git a/engines/sci/sound/drivers/gm_names.h b/engines/sci/sound/drivers/gm_names.h
new file mode 100644
index 0000000000..b7883494f6
--- /dev/null
+++ b/engines/sci/sound/drivers/gm_names.h
@@ -0,0 +1,220 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_SOUND_DRIVERS_GM_NAMES_H
+#define SCI_SOUND_DRIVERS_GM_NAMES_H
+
+namespace Sci {
+
+static const char *GmInstrumentNames[] = {
+ /*000*/ "Acoustic Grand Piano",
+ /*001*/ "Bright Acoustic Piano",
+ /*002*/ "Electric Grand Piano",
+ /*003*/ "Honky-tonk Piano",
+ /*004*/ "Electric Piano 1",
+ /*005*/ "Electric Piano 2",
+ /*006*/ "Harpsichord",
+ /*007*/ "Clavinet",
+ /*008*/ "Celesta",
+ /*009*/ "Glockenspiel",
+ /*010*/ "Music Box",
+ /*011*/ "Vibraphone",
+ /*012*/ "Marimba",
+ /*013*/ "Xylophone",
+ /*014*/ "Tubular Bells",
+ /*015*/ "Dulcimer",
+ /*016*/ "Drawbar Organ",
+ /*017*/ "Percussive Organ",
+ /*018*/ "Rock Organ",
+ /*019*/ "Church Organ",
+ /*020*/ "Reed Organ",
+ /*021*/ "Accordion",
+ /*022*/ "Harmonica",
+ /*023*/ "Tango Accordion",
+ /*024*/ "Acoustic Guitar (nylon)",
+ /*025*/ "Acoustic Guitar (steel)",
+ /*026*/ "Electric Guitar (jazz)",
+ /*027*/ "Electric Guitar (clean)",
+ /*028*/ "Electric Guitar (muted)",
+ /*029*/ "Overdriven Guitar",
+ /*030*/ "Distortion Guitar",
+ /*031*/ "Guitar Harmonics",
+ /*032*/ "Acoustic Bass",
+ /*033*/ "Electric Bass (finger)",
+ /*034*/ "Electric Bass (pick)",
+ /*035*/ "Fretless Bass",
+ /*036*/ "Slap Bass 1",
+ /*037*/ "Slap Bass 2",
+ /*038*/ "Synth Bass 1",
+ /*039*/ "Synth Bass 2",
+ /*040*/ "Violin",
+ /*041*/ "Viola",
+ /*042*/ "Cello",
+ /*043*/ "Contrabass",
+ /*044*/ "Tremolo Strings",
+ /*045*/ "Pizzicato Strings",
+ /*046*/ "Orchestral Harp",
+ /*047*/ "Timpani",
+ /*048*/ "String Ensemble 1",
+ /*049*/ "String Ensemble 2",
+ /*050*/ "SynthStrings 1",
+ /*051*/ "SynthStrings 2",
+ /*052*/ "Choir Aahs",
+ /*053*/ "Voice Oohs",
+ /*054*/ "Synth Voice",
+ /*055*/ "Orchestra Hit",
+ /*056*/ "Trumpet",
+ /*057*/ "Trombone",
+ /*058*/ "Tuba",
+ /*059*/ "Muted Trumpet",
+ /*060*/ "French Horn",
+ /*061*/ "Brass Section",
+ /*062*/ "SynthBrass 1",
+ /*063*/ "SynthBrass 2",
+ /*064*/ "Soprano Sax",
+ /*065*/ "Alto Sax",
+ /*066*/ "Tenor Sax",
+ /*067*/ "Baritone Sax",
+ /*068*/ "Oboe",
+ /*069*/ "English Horn",
+ /*070*/ "Bassoon",
+ /*071*/ "Clarinet",
+ /*072*/ "Piccolo",
+ /*073*/ "Flute",
+ /*074*/ "Recorder",
+ /*075*/ "Pan Flute",
+ /*076*/ "Blown Bottle",
+ /*077*/ "Shakuhachi",
+ /*078*/ "Whistle",
+ /*079*/ "Ocarina",
+ /*080*/ "Lead 1 (square)",
+ /*081*/ "Lead 2 (sawtooth)",
+ /*082*/ "Lead 3 (calliope)",
+ /*083*/ "Lead 4 (chiff)",
+ /*084*/ "Lead 5 (charang)",
+ /*085*/ "Lead 6 (voice)",
+ /*086*/ "Lead 7 (fifths)",
+ /*087*/ "Lead 8 (bass+lead)",
+ /*088*/ "Pad 1 (new age)",
+ /*089*/ "Pad 2 (warm)",
+ /*090*/ "Pad 3 (polysynth)",
+ /*091*/ "Pad 4 (choir)",
+ /*092*/ "Pad 5 (bowed)",
+ /*093*/ "Pad 6 (metallic)",
+ /*094*/ "Pad 7 (halo)",
+ /*095*/ "Pad 8 (sweep)",
+ /*096*/ "FX 1 (rain)",
+ /*097*/ "FX 2 (soundtrack)",
+ /*098*/ "FX 3 (crystal)",
+ /*099*/ "FX 4 (atmosphere)",
+ /*100*/ "FX 5 (brightness)",
+ /*101*/ "FX 6 (goblins)",
+ /*102*/ "FX 7 (echoes)",
+ /*103*/ "FX 8 (sci-fi)",
+ /*104*/ "Sitar",
+ /*105*/ "Banjo",
+ /*106*/ "Shamisen",
+ /*107*/ "Koto",
+ /*108*/ "Kalimba",
+ /*109*/ "Bag pipe",
+ /*110*/ "Fiddle",
+ /*111*/ "Shannai",
+ /*112*/ "Tinkle Bell",
+ /*113*/ "Agogo",
+ /*114*/ "Steel Drums",
+ /*115*/ "Woodblock",
+ /*116*/ "Taiko Drum",
+ /*117*/ "Melodic Tom",
+ /*118*/ "Synth Drum",
+ /*119*/ "Reverse Cymbal",
+ /*120*/ "Guitar Fret Noise",
+ /*121*/ "Breath Noise",
+ /*122*/ "Seashore",
+ /*123*/ "Bird Tweet",
+ /*124*/ "Telephone Ring",
+ /*125*/ "Helicopter",
+ /*126*/ "Applause",
+ /*127*/ "Gunshot"
+};
+
+// The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI
+static const char *GmPercussionNames[] = {
+ /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*30*/ 0, 0, 0, 0, 0,
+ // The preceeding percussions are not covered by the GM standard
+ /*35*/ "Acoustic Bass Drum",
+ /*36*/ "Bass Drum 1",
+ /*37*/ "Side Stick",
+ /*38*/ "Acoustic Snare",
+ /*39*/ "Hand Clap",
+ /*40*/ "Electric Snare",
+ /*41*/ "Low Floor Tom",
+ /*42*/ "Closed Hi-Hat",
+ /*43*/ "High Floor Tom",
+ /*44*/ "Pedal Hi-Hat",
+ /*45*/ "Low Tom",
+ /*46*/ "Open Hi-Hat",
+ /*47*/ "Low-Mid Tom",
+ /*48*/ "Hi-Mid Tom",
+ /*49*/ "Crash Cymbal 1",
+ /*50*/ "High Tom",
+ /*51*/ "Ride Cymbal 1",
+ /*52*/ "Chinese Cymbal",
+ /*53*/ "Ride Bell",
+ /*54*/ "Tambourine",
+ /*55*/ "Splash Cymbal",
+ /*56*/ "Cowbell",
+ /*57*/ "Crash Cymbal 2",
+ /*58*/ "Vibraslap",
+ /*59*/ "Ride Cymbal 2",
+ /*60*/ "Hi Bongo",
+ /*61*/ "Low Bongo",
+ /*62*/ "Mute Hi Conga",
+ /*63*/ "Open Hi Conga",
+ /*64*/ "Low Conga",
+ /*65*/ "High Timbale",
+ /*66*/ "Low Timbale",
+ /*67*/ "High Agogo",
+ /*68*/ "Low Agogo",
+ /*69*/ "Cabasa",
+ /*70*/ "Maracas",
+ /*71*/ "Short Whistle",
+ /*72*/ "Long Whistle",
+ /*73*/ "Short Guiro",
+ /*74*/ "Long Guiro",
+ /*75*/ "Claves",
+ /*76*/ "Hi Wood Block",
+ /*77*/ "Low Wood Block",
+ /*78*/ "Mute Cuica",
+ /*79*/ "Open Cuica",
+ /*80*/ "Mute Triangle",
+ /*81*/ "Open Triangle"
+};
+
+} // End of namespace Sci
+
+#endif // SCI_SOUND_DRIVERS_GM_NAMES_H
diff --git a/engines/sci/sound/drivers/map-mt32-to-gm.h b/engines/sci/sound/drivers/map-mt32-to-gm.h
index 05d1aeba24..f7a6256ba4 100644
--- a/engines/sci/sound/drivers/map-mt32-to-gm.h
+++ b/engines/sci/sound/drivers/map-mt32-to-gm.h
@@ -23,11 +23,16 @@
*
*/
+#ifndef SCI_SOUND_DRIVERS_MAP_MT32_TO_GM_H
+#define SCI_SOUND_DRIVERS_MAP_MT32_TO_GM_H
+
namespace Sci {
-/* Patch not mapped */
+#include "common/list.h"
+
+// Patch not mapped
#define MIDI_UNMAPPED 0xff
-/* Patch mapped to rhythm key */
+// Patch mapped to rhythm key
#define MIDI_MAPPED_TO_RHYTHM 0xfe
struct Mt32ToGmMap {
@@ -36,193 +41,6 @@ struct Mt32ToGmMap {
uint8 gmRhythmKey;
};
-static const char *GmInstrumentNames[] = {
- /*000*/ "Acoustic Grand Piano",
- /*001*/ "Bright Acoustic Piano",
- /*002*/ "Electric Grand Piano",
- /*003*/ "Honky-tonk Piano",
- /*004*/ "Electric Piano 1",
- /*005*/ "Electric Piano 2",
- /*006*/ "Harpsichord",
- /*007*/ "Clavinet",
- /*008*/ "Celesta",
- /*009*/ "Glockenspiel",
- /*010*/ "Music Box",
- /*011*/ "Vibraphone",
- /*012*/ "Marimba",
- /*013*/ "Xylophone",
- /*014*/ "Tubular Bells",
- /*015*/ "Dulcimer",
- /*016*/ "Drawbar Organ",
- /*017*/ "Percussive Organ",
- /*018*/ "Rock Organ",
- /*019*/ "Church Organ",
- /*020*/ "Reed Organ",
- /*021*/ "Accordion",
- /*022*/ "Harmonica",
- /*023*/ "Tango Accordion",
- /*024*/ "Acoustic Guitar (nylon)",
- /*025*/ "Acoustic Guitar (steel)",
- /*026*/ "Electric Guitar (jazz)",
- /*027*/ "Electric Guitar (clean)",
- /*028*/ "Electric Guitar (muted)",
- /*029*/ "Overdriven Guitar",
- /*030*/ "Distortion Guitar",
- /*031*/ "Guitar Harmonics",
- /*032*/ "Acoustic Bass",
- /*033*/ "Electric Bass (finger)",
- /*034*/ "Electric Bass (pick)",
- /*035*/ "Fretless Bass",
- /*036*/ "Slap Bass 1",
- /*037*/ "Slap Bass 2",
- /*038*/ "Synth Bass 1",
- /*039*/ "Synth Bass 2",
- /*040*/ "Violin",
- /*041*/ "Viola",
- /*042*/ "Cello",
- /*043*/ "Contrabass",
- /*044*/ "Tremolo Strings",
- /*045*/ "Pizzicato Strings",
- /*046*/ "Orchestral Harp",
- /*047*/ "Timpani",
- /*048*/ "String Ensemble 1",
- /*049*/ "String Ensemble 2",
- /*050*/ "SynthStrings 1",
- /*051*/ "SynthStrings 2",
- /*052*/ "Choir Aahs",
- /*053*/ "Voice Oohs",
- /*054*/ "Synth Voice",
- /*055*/ "Orchestra Hit",
- /*056*/ "Trumpet",
- /*057*/ "Trombone",
- /*058*/ "Tuba",
- /*059*/ "Muted Trumpet",
- /*060*/ "French Horn",
- /*061*/ "Brass Section",
- /*062*/ "SynthBrass 1",
- /*063*/ "SynthBrass 2",
- /*064*/ "Soprano Sax",
- /*065*/ "Alto Sax",
- /*066*/ "Tenor Sax",
- /*067*/ "Baritone Sax",
- /*068*/ "Oboe",
- /*069*/ "English Horn",
- /*070*/ "Bassoon",
- /*071*/ "Clarinet",
- /*072*/ "Piccolo",
- /*073*/ "Flute",
- /*074*/ "Recorder",
- /*075*/ "Pan Flute",
- /*076*/ "Blown Bottle",
- /*077*/ "Shakuhachi",
- /*078*/ "Whistle",
- /*079*/ "Ocarina",
- /*080*/ "Lead 1 (square)",
- /*081*/ "Lead 2 (sawtooth)",
- /*082*/ "Lead 3 (calliope)",
- /*083*/ "Lead 4 (chiff)",
- /*084*/ "Lead 5 (charang)",
- /*085*/ "Lead 6 (voice)",
- /*086*/ "Lead 7 (fifths)",
- /*087*/ "Lead 8 (bass+lead)",
- /*088*/ "Pad 1 (new age)",
- /*089*/ "Pad 2 (warm)",
- /*090*/ "Pad 3 (polysynth)",
- /*091*/ "Pad 4 (choir)",
- /*092*/ "Pad 5 (bowed)",
- /*093*/ "Pad 6 (metallic)",
- /*094*/ "Pad 7 (halo)",
- /*095*/ "Pad 8 (sweep)",
- /*096*/ "FX 1 (rain)",
- /*097*/ "FX 2 (soundtrack)",
- /*098*/ "FX 3 (crystal)",
- /*099*/ "FX 4 (atmosphere)",
- /*100*/ "FX 5 (brightness)",
- /*101*/ "FX 6 (goblins)",
- /*102*/ "FX 7 (echoes)",
- /*103*/ "FX 8 (sci-fi)",
- /*104*/ "Sitar",
- /*105*/ "Banjo",
- /*106*/ "Shamisen",
- /*107*/ "Koto",
- /*108*/ "Kalimba",
- /*109*/ "Bag pipe",
- /*110*/ "Fiddle",
- /*111*/ "Shannai",
- /*112*/ "Tinkle Bell",
- /*113*/ "Agogo",
- /*114*/ "Steel Drums",
- /*115*/ "Woodblock",
- /*116*/ "Taiko Drum",
- /*117*/ "Melodic Tom",
- /*118*/ "Synth Drum",
- /*119*/ "Reverse Cymbal",
- /*120*/ "Guitar Fret Noise",
- /*121*/ "Breath Noise",
- /*122*/ "Seashore",
- /*123*/ "Bird Tweet",
- /*124*/ "Telephone Ring",
- /*125*/ "Helicopter",
- /*126*/ "Applause",
- /*127*/ "Gunshot"
-};
-
-/* The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI */
-static const char *GmPercussionNames[] = {
- /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /*30*/ 0, 0, 0, 0, 0,
- /* The preceeding percussions are not covered by the GM standard */
- /*35*/ "Acoustic Bass Drum",
- /*36*/ "Bass Drum 1",
- /*37*/ "Side Stick",
- /*38*/ "Acoustic Snare",
- /*39*/ "Hand Clap",
- /*40*/ "Electric Snare",
- /*41*/ "Low Floor Tom",
- /*42*/ "Closed Hi-Hat",
- /*43*/ "High Floor Tom",
- /*44*/ "Pedal Hi-Hat",
- /*45*/ "Low Tom",
- /*46*/ "Open Hi-Hat",
- /*47*/ "Low-Mid Tom",
- /*48*/ "Hi-Mid Tom",
- /*49*/ "Crash Cymbal 1",
- /*50*/ "High Tom",
- /*51*/ "Ride Cymbal 1",
- /*52*/ "Chinese Cymbal",
- /*53*/ "Ride Bell",
- /*54*/ "Tambourine",
- /*55*/ "Splash Cymbal",
- /*56*/ "Cowbell",
- /*57*/ "Crash Cymbal 2",
- /*58*/ "Vibraslap",
- /*59*/ "Ride Cymbal 2",
- /*60*/ "Hi Bongo",
- /*61*/ "Low Bongo",
- /*62*/ "Mute Hi Conga",
- /*63*/ "Open Hi Conga",
- /*64*/ "Low Conga",
- /*65*/ "High Timbale",
- /*66*/ "Low Timbale",
- /*67*/ "High Agogo",
- /*68*/ "Low Agogo",
- /*69*/ "Cabasa",
- /*70*/ "Maracas",
- /*71*/ "Short Whistle",
- /*72*/ "Long Whistle",
- /*73*/ "Short Guiro",
- /*74*/ "Long Guiro",
- /*75*/ "Claves",
- /*76*/ "Hi Wood Block",
- /*77*/ "Low Wood Block",
- /*78*/ "Mute Cuica",
- /*79*/ "Open Cuica",
- /*80*/ "Mute Triangle",
- /*81*/ "Open Triangle"
-};
-
/*******************************************
* Fancy instrument mappings begin here... *
*******************************************/
@@ -344,19 +162,19 @@ static const Mt32ToGmMap Mt32PresetTimbreMaps[] = {
/*112*/ {"Timpani ", 47, MIDI_UNMAPPED},
/*113*/ {"MelodicTom", 117, MIDI_UNMAPPED},
/*114*/ {"Deep Snare", MIDI_MAPPED_TO_RHYTHM, 38},
- /*115*/ {"Elec Perc1", 115, MIDI_UNMAPPED}, /* ? */
- /*116*/ {"Elec Perc2", 118, MIDI_UNMAPPED}, /* ? */
+ /*115*/ {"Elec Perc1", 115, MIDI_UNMAPPED}, // ?
+ /*116*/ {"Elec Perc2", 118, MIDI_UNMAPPED}, // ?
/*117*/ {"Taiko ", 116, MIDI_UNMAPPED},
/*118*/ {"Taiko Rim ", 118, MIDI_UNMAPPED},
/*119*/ {"Cymbal ", MIDI_MAPPED_TO_RHYTHM, 51},
- /*120*/ {"Castanets ", MIDI_UNMAPPED, MIDI_UNMAPPED},
+ /*120*/ {"Castanets ", MIDI_MAPPED_TO_RHYTHM, 75}, // approximation
/*121*/ {"Triangle ", 112, MIDI_UNMAPPED},
/*122*/ {"Orche Hit ", 55, MIDI_UNMAPPED},
/*123*/ {"Telephone ", 124, MIDI_UNMAPPED},
/*124*/ {"Bird Tweet", 123, MIDI_UNMAPPED},
- /*125*/ {"OneNoteJam", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? */
+ /*125*/ {"OneNoteJam", 8, MIDI_UNMAPPED}, // approximation
/*126*/ {"WaterBells", 98, MIDI_UNMAPPED},
- /*127*/ {"JungleTune", MIDI_UNMAPPED, MIDI_UNMAPPED} /* ? */
+ /*127*/ {"JungleTune", 75, MIDI_UNMAPPED} // approximation
};
static const Mt32ToGmMap Mt32RhythmTimbreMaps[] = {
@@ -414,139 +232,146 @@ static const uint8 Mt32PresetRhythmKeymap[] = {
? - Where do I map this one?
?? - Any good ideas?
??? - I'm clueless?
- R - Rhythm... */
+ R - Rhythm...
+*/
static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = {
- {"AccPnoKA2 ", 1, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 35}, /* R (PQ2) */
- {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (PQ2) */
- {"AcouPnoKA ", 0, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"BASS ", 32, MIDI_UNMAPPED}, /* + (LSL3) */
- {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (LB1) */
- {"BEACH WAVE", 122, MIDI_UNMAPPED}, /* + (LSL3) */
+ {"AccPnoKA2 ", 1, MIDI_UNMAPPED}, // ++ (KQ1)
+ {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 35}, // R (PQ2)
+ {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 38}, // R (PQ2)
+ {"AcouPnoKA ", 0, MIDI_UNMAPPED}, // ++ (KQ1)
+ {"BASS ", 32, MIDI_UNMAPPED}, // + (LSL3)
+ {"BASSOONPCM", 70, MIDI_UNMAPPED}, // + (LB1)
+ {"BEACH WAVE", 122, MIDI_UNMAPPED}, // + (LSL3)
{"BagPipes ", 109, MIDI_UNMAPPED},
- {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (QFG1) */
- {"BassoonKA ", 70, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (Iceman) */
- {"Bells MS", 112, MIDI_UNMAPPED}, /* + (QFG1) */
- {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (LB1) */
+ {"BassPizzMS", 45, MIDI_UNMAPPED}, // ++ (QFG1)
+ {"BassoonKA ", 70, MIDI_UNMAPPED}, // ++ (KQ1)
+ {"Bell MS", 112, MIDI_UNMAPPED}, // ++ (Iceman)
+ {"Bells MS", 112, MIDI_UNMAPPED}, // + (QFG1)
+ {"Big Bell ", 14, MIDI_UNMAPPED}, // + (LB1)
{"Bird Tweet", 123, MIDI_UNMAPPED},
- {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (Iceman) */
- {"CLAPPING ", 126, MIDI_UNMAPPED}, /* ++ (LSL3) */
- {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, /* R (Hoyle) */
- {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (QFG1) */
- {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (Camelot) */
- {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (Iceman) */
+ {"BrsSect MS", 61, MIDI_UNMAPPED}, // +++ (Iceman)
+ {"CLAPPING ", 126, MIDI_UNMAPPED}, // ++ (LSL3)
+ {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, // R (Hoyle)
+ {"Calliope ", 82, MIDI_UNMAPPED}, // +++ (QFG1)
+ {"CelticHarp", 46, MIDI_UNMAPPED}, // ++ (Camelot)
+ {"Chicago MS", 1, MIDI_UNMAPPED}, // ++ (Iceman)
{"Chop ", 117, MIDI_UNMAPPED},
- {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (Camelot) */
+ {"Chorale MS", 52, MIDI_UNMAPPED}, // + (Camelot)
{"ClarinetMS", 71, MIDI_UNMAPPED},
- {"Claves ", MIDI_MAPPED_TO_RHYTHM, 75}, /* R (PQ2) */
- {"Claw MS", 118, MIDI_UNMAPPED}, /* + (QFG1) */
- {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (LB1) */
- {"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (KQ1) */
- {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 64}, /* R (QFG1) */
- {"CoolPhone ", 124, MIDI_UNMAPPED}, /* ++ (LSL3) */
- {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
- {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
- {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (LB1) */
- {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 57}, /* R +++ (Iceman) */
- {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1) */
- {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (Camelot, QFG1) */
- {"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 57}, /* R ? (KQ1) */
- {"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* R ? (LSL3) */
- {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Hoyle) */
- {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (Iceman) */
- {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (Iceman) */
- {"E Bass MS", 33, MIDI_UNMAPPED}, /* + (SQ3) */
+ {"Claves ", MIDI_MAPPED_TO_RHYTHM, 75}, // R (PQ2)
+ {"Claw MS", 118, MIDI_UNMAPPED}, // + (QFG1)
+ {"ClockBell ", 14, MIDI_UNMAPPED}, // + (LB1)
+ {"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 55}, // R ? (KQ1)
+ {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 64}, // R (QFG1)
+ {"CoolPhone ", 124, MIDI_UNMAPPED}, // ++ (LSL3)
+ {"CracklesMS", 115, MIDI_UNMAPPED}, // ? (Camelot, QFG1)
+ {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ??? (KQ1)
+ {"Cricket ", 120, MIDI_UNMAPPED}, // ? (LB1)
+ {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 57}, // R +++ (Iceman)
+ {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (QFG1)
+ {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 55}, // R ? (Camelot, QFG1)
+ {"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 57}, // R ? (KQ1)
+ {"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, // R ? (LSL3)
+ {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (Hoyle)
+ {"DirtGtr MS", 30, MIDI_UNMAPPED}, // + (Iceman)
+ {"DirtGtr2MS", 29, MIDI_UNMAPPED}, // + (Iceman)
+ {"E Bass MS", 33, MIDI_UNMAPPED}, // + (SQ3)
{"ElecBassMS", 33, MIDI_UNMAPPED},
- {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (Iceman) */
+ {"ElecGtr MS", 27, MIDI_UNMAPPED}, // ++ (Iceman)
{"EnglHornMS", 69, MIDI_UNMAPPED},
{"FantasiaKA", 88, MIDI_UNMAPPED},
- {"Fantasy ", 99, MIDI_UNMAPPED}, /* + (PQ2) */
- {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (Camelot, QFG1) */
- {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (Iceman) */
- {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (Iceman) */
- {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (QFG1) */
- {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (QFG1) */
- {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (QFG1) */
+ {"Fantasy ", 99, MIDI_UNMAPPED}, // + (PQ2)
+ {"Fantasy2MS", 99, MIDI_UNMAPPED}, // ++ (Camelot, QFG1)
+ {"Filter MS", 95, MIDI_UNMAPPED}, // +++ (Iceman)
+ {"Filter2 MS", 95, MIDI_UNMAPPED}, // ++ (Iceman)
+ {"Flame2 MS", 121, MIDI_UNMAPPED}, // ? (QFG1)
+ {"Flames MS", 121, MIDI_UNMAPPED}, // ? (QFG1)
+ {"Flute MS", 73, MIDI_UNMAPPED}, // +++ (QFG1)
{"FogHorn MS", 58, MIDI_UNMAPPED},
- {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (QFG1) */
- {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (LB1) */
+ {"FrHorn1 MS", 60, MIDI_UNMAPPED}, // +++ (QFG1)
+ {"FunnyTrmp ", 56, MIDI_UNMAPPED}, // ++ (LB1)
{"GameSnd MS", 80, MIDI_UNMAPPED},
- {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (QFG1) */
- {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1) */
- {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (Hoyle) */
- {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (Iceman) */
- {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
- {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
- {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (Iceman) */
- {"KNIFE ", 120, MIDI_UNMAPPED}, /* ? (LSL3) */
- {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (QFG1) */
- {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
- {"Koto ", 107, MIDI_UNMAPPED}, /* +++ (PQ2) */
- {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (QFG1) */
- {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (QFG1) */
- {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (Iceman) */
- {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (Iceman) */
- {"OCEANSOUND", 122, MIDI_UNMAPPED}, /* + (LSL3) */
- {"Oboe 2001 ", 68, MIDI_UNMAPPED}, /* + (PQ2) */
- {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (Iceman) */
- {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (Iceman) */
- {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (LB1) */
- {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, /* R ? (Iceman) */
- {"PiccoloKA ", 72, MIDI_UNMAPPED}, /* +++ (KQ1) */
+ {"Glock MS", 9, MIDI_UNMAPPED}, // +++ (QFG1)
+ {"Gunshot ", 127, MIDI_UNMAPPED}, // +++ (LB1)
+ {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (QFG1)
+ {"Harmonica2", 22, MIDI_UNMAPPED}, // +++ (LB1)
+ {"Harpsi 1 ", 6, MIDI_UNMAPPED}, // + (Hoyle)
+ {"Harpsi 2 ", 6, MIDI_UNMAPPED}, // +++ (LB1)
+ {"Heart MS", 116, MIDI_UNMAPPED}, // ? (Iceman)
+ {"Horse1 MS", 115, MIDI_UNMAPPED}, // ? (Camelot, QFG1)
+ {"Horse2 MS", 115, MIDI_UNMAPPED}, // ? (Camelot, QFG1)
+ {"InHale MS", 121, MIDI_UNMAPPED}, // ++ (Iceman)
+ {"KNIFE ", 120, MIDI_UNMAPPED}, // ? (LSL3)
+ {"KenBanjo ", 105, MIDI_UNMAPPED}, // +++ (LB1)
+ {"Kiss MS", 25, MIDI_UNMAPPED}, // ++ (QFG1)
+ {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ??? (KQ1)
+ {"Koto ", 107, MIDI_UNMAPPED}, // +++ (PQ2)
+ {"Laser MS", 81, MIDI_UNMAPPED}, // ?? (QFG1)
+ {"Meeps MS", 62, MIDI_UNMAPPED}, // ? (QFG1)
+ {"MTrak MS", 62, MIDI_UNMAPPED}, // ?? (Iceman)
+ {"MachGun MS", 127, MIDI_UNMAPPED}, // ? (Iceman)
+ {"OCEANSOUND", 122, MIDI_UNMAPPED}, // + (LSL3)
+ {"Oboe 2001 ", 68, MIDI_UNMAPPED}, // + (PQ2)
+ {"Ocean MS", 122, MIDI_UNMAPPED}, // + (Iceman)
+ {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, // ? (Iceman)
+ {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (LB1)
+ {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, // R ? (Iceman)
+ {"PiccoloKA ", 72, MIDI_UNMAPPED}, // +++ (KQ1)
{"PinkBassMS", 39, MIDI_UNMAPPED},
- {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (LB1) */
- {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
- {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (QFG1) */
- {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (LauraBow1, Camelot) */
- {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (Camelot) */
- {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (LB1) */
- {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Camelot) */
+ {"Pizz2 ", 45, MIDI_UNMAPPED}, // ++ (LB1)
+ {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (KQ1)
+ {"Raspbry MS", 81, MIDI_UNMAPPED}, // ? (QFG1)
+ {"RatSqueek ", 72, MIDI_UNMAPPED}, // ? (LauraBow1, Camelot)
+ {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, // +++ (LB1)
+ {"RecorderMS", 74, MIDI_UNMAPPED}, // +++ (Camelot)
+ {"Red Baron ", 125, MIDI_UNMAPPED}, // ? (LB1)
+ {"ReedPipMS ", 20, MIDI_UNMAPPED}, // +++ (Camelot)
{"RevCymb MS", 119, MIDI_UNMAPPED},
- {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (LB1) */
- {"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 37}, /* R */
- {"SHOWER ", 52, MIDI_UNMAPPED}, /* ? (LSL3) */
- {"SQ Bass MS", 32, MIDI_UNMAPPED}, /* + (SQ3) */
- {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (Iceman) */
- {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (Iceman) */
- {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (QFG1) */
- {"Some Birds", 123, MIDI_UNMAPPED}, /* + (LB1) */
- {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (Iceman) */
- {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (Camelot) */
+ {"RifleShot ", 127, MIDI_UNMAPPED}, // + (LB1)
+ {"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 37}, // R
+ {"SHOWER ", 52, MIDI_UNMAPPED}, // ? (LSL3)
+ {"SQ Bass MS", 32, MIDI_UNMAPPED}, // + (SQ3)
+ {"ShakuVibMS", 79, MIDI_UNMAPPED}, // + (Iceman)
+ {"SlapBassMS", 36, MIDI_UNMAPPED}, // +++ (Iceman)
+ {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 38}, // R (QFG1)
+ {"Some Birds", 123, MIDI_UNMAPPED}, // + (LB1)
+ {"Sonar MS", 78, MIDI_UNMAPPED}, // ? (Iceman)
+ {"Soundtrk2 ", 97, MIDI_UNMAPPED}, // +++ (LB1)
+ {"Soundtrack", 97, MIDI_UNMAPPED}, // ++ (Camelot)
{"SqurWaveMS", 80, MIDI_UNMAPPED},
- {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (Iceman) */
- {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (Iceman) */
- {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (QFG1) */
- {"String MS", 45, MIDI_UNMAPPED}, /* + (Camelot) */
+ {"StabBassMS", 34, MIDI_UNMAPPED}, // + (Iceman)
+ {"SteelDrmMS", 114, MIDI_UNMAPPED}, // +++ (Iceman)
+ {"StrSect1MS", 48, MIDI_UNMAPPED}, // ++ (QFG1)
+ {"String MS", 45, MIDI_UNMAPPED}, // + (Camelot)
{"Syn-Choir ", 91, MIDI_UNMAPPED},
- {"Syn Brass4", 63, MIDI_UNMAPPED}, /* ++ (PQ2) */
+ {"Syn Brass4", 63, MIDI_UNMAPPED}, // ++ (PQ2)
{"SynBass MS", 38, MIDI_UNMAPPED},
- {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (LB1, QFG1) */
- {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (QFG1) */
- {"Taiko ", 116, 35}, /* +++ (Camelot) */
- {"Taiko Rim ", 118, 37}, /* +++ (LSL3) */
- {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Tom MS", 117, 48}, /* +++ (Iceman) */
- {"Toms MS", 117, 48}, /* +++ (Camelot, QFG1) */
- {"Tpt1prtl ", 56, MIDI_UNMAPPED}, /* +++ (KQ1) */
- {"TriangleMS", 112, 81}, /* R (Camelot) */
- {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (Camelot) */
- {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, /* + (Iceman) */
- {"WaterBells", 98, MIDI_UNMAPPED}, /* + (PQ2) */
- {"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
- {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (LB1) */
- {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (LB1) */
- {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1, Iceman) */
- {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Camelot) */
- {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (LB1) */
- {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Camelot, QFG1, Iceman) */
+ {"SwmpBackgr", 120, MIDI_UNMAPPED}, // ?? (LB1, QFG1)
+ {"T-Bone2 MS", 57, MIDI_UNMAPPED}, // +++ (QFG1)
+ {"Taiko ", 116, 35}, // +++ (Camelot)
+ {"Taiko Rim ", 118, 37}, // +++ (LSL3)
+ {"Timpani1 ", 47, MIDI_UNMAPPED}, // +++ (LB1)
+ {"Tom MS", 117, 48}, // +++ (Iceman)
+ {"Toms MS", 117, 48}, // +++ (Camelot, QFG1)
+ {"Tpt1prtl ", 56, MIDI_UNMAPPED}, // +++ (KQ1)
+ {"TriangleMS", 112, 81}, // R (Camelot)
+ {"Trumpet 1 ", 56, MIDI_UNMAPPED}, // +++ (Camelot)
+ {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, // + (Iceman)
+ {"Warm Pad" , 89, MIDI_UNMAPPED}, // ++ (PQ3)
+ {"WaterBells", 98, MIDI_UNMAPPED}, // + (PQ2)
+ {"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (KQ1)
+ {"Whiporill ", 123, MIDI_UNMAPPED}, // + (LB1)
+ {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (LB1)
+ {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (QFG1, Iceman)
+ {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (Camelot)
+ {"Woodpecker", 115, MIDI_UNMAPPED}, // ? (LB1)
+ {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (Camelot, QFG1, Iceman)
{0, 0, 0}
};
+ typedef Common::List<Mt32ToGmMap> Mt32ToGmMapList;
+ extern Mt32ToGmMapList *Mt32dynamicMappings;
+
} // End of namespace Sci
+
+#endif // SCI_SOUND_DRIVERS_MAP_MT32_TO_GM_H
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 8ba7a6a352..ed3a1e25d2 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -27,16 +27,21 @@
#include "common/config-manager.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "sound/fmopl.h"
#include "sound/softsynth/emumidi.h"
#include "sci/resource.h"
+#include "sci/engine/features.h"
+#include "sci/sound/drivers/gm_names.h"
#include "sci/sound/drivers/mididriver.h"
#include "sci/sound/drivers/map-mt32-to-gm.h"
namespace Sci {
+Mt32ToGmMapList *Mt32dynamicMappings = NULL;
+
class MidiPlayer_Midi : public MidiPlayer {
public:
enum {
@@ -54,12 +59,17 @@ public:
void sysEx(const byte *msg, uint16 length);
bool hasRhythmChannel() const { return true; }
byte getPlayId() const;
- int getPolyphony() const { return kVoices; }
+ int getPolyphony() const {
+ if (g_sci && g_sci->_features->useAltWinGMSound())
+ return 16;
+ else
+ return kVoices;
+ }
int getFirstChannel() const;
int getLastChannel() const;
void setVolume(byte volume);
int getVolume();
- void setReverb(byte reverb);
+ void setReverb(int8 reverb);
void playSwitch(bool play);
private:
@@ -131,10 +141,21 @@ MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _pla
_sysExBuf[1] = 0x10;
_sysExBuf[2] = 0x16;
_sysExBuf[3] = 0x12;
+
+ Mt32dynamicMappings = new Mt32ToGmMapList();
}
MidiPlayer_Midi::~MidiPlayer_Midi() {
delete _driver;
+
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ delete[] (*it).name;
+ (*it).name = 0;
+ }
+
+ Mt32dynamicMappings->clear();
+ delete Mt32dynamicMappings;
}
void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
@@ -304,8 +325,10 @@ void MidiPlayer_Midi::send(uint32 b) {
// In early SCI0, we may also get events for AdLib rhythm channels.
// While an MT-32 would ignore those with the default channel mapping,
// we filter these out for the benefit of other MIDI devices.
- if (channel < 1 || channel > 9)
- return;
+ if (_version == SCI_VERSION_0_EARLY) {
+ if (channel < 1 || channel > 9)
+ return;
+ }
switch (command) {
case 0x80:
@@ -320,6 +343,10 @@ void MidiPlayer_Midi::send(uint32 b) {
case 0xc0:
setPatch(channel, op1);
break;
+ // The original MIDI driver from sierra ignores aftertouch completely, so should we
+ case 0xa0: // Polyphonic key pressure (aftertouch)
+ case 0xd0: // Channel pressure (aftertouch)
+ break;
case 0xe0:
_driver->send(b);
break;
@@ -358,10 +385,13 @@ int MidiPlayer_Midi::getVolume() {
return _masterVolume;
}
-void MidiPlayer_Midi::setReverb(byte reverb) {
- _reverb = CLIP<byte>(reverb, 0, kReverbConfigNr - 1);
- if (_hasReverb)
- sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true);
+void MidiPlayer_Midi::setReverb(int8 reverb) {
+ assert(reverb < kReverbConfigNr);
+
+ if (_hasReverb && (_reverb != reverb))
+ sendMt32SysEx(0x100001, _reverbConfig[reverb], 3, true);
+
+ _reverb = reverb;
}
void MidiPlayer_Midi::playSwitch(bool play) {
@@ -420,9 +450,9 @@ void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStrea
_sysExBuf[7 + i] = str->readByte();
for (int i = 4; i < 7 + len; i++)
- chk += _sysExBuf[i];
+ chk -= _sysExBuf[i];
- _sysExBuf[7 + len] = 128 - chk % 128;
+ _sysExBuf[7 + len] = chk & 0x7f;
if (noDelay)
_driver->sysEx(_sysExBuf, len + 8);
@@ -450,17 +480,18 @@ void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) {
setMt32Volume(volume);
// Reverb default only used in (roughly) SCI0/SCI01
- _reverb = str->readByte();
+ byte reverb = str->readByte();
+
_hasReverb = true;
// Skip reverb SysEx message
str->seek(11, SEEK_CUR);
- // Read reverb data
- for (int i = 0; i < kReverbConfigNr; i++) {
- _reverbConfig[i][0] = str->readByte();
- _reverbConfig[i][1] = str->readByte();
- _reverbConfig[i][2] = str->readByte();
+ // Read reverb data (stored vertically - patch #3117434)
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < kReverbConfigNr; i++) {
+ _reverbConfig[i][j] = str->readByte();
+ }
}
// Patches 1-48
@@ -488,6 +519,10 @@ void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) {
sendMt32SysEx(0x100004, str, 9);
}
+ // Reverb for SCI0
+ if (_version <= SCI_VERSION_0_LATE)
+ setReverb(reverb);
+
// Send after-SysEx text
str->seek(0);
sendMt32SysEx(0x200000, str, 20);
@@ -615,22 +650,40 @@ void MidiPlayer_Midi::readMt32DrvData() {
byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) {
int i = 0;
+ if (Mt32dynamicMappings != NULL) {
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ if (scumm_strnicmp(iname, (*it).name, 10) == 0)
+ return getGmInstrument((*it));
+ }
+ }
+
while (Mt32MemoryTimbreMaps[i].name) {
if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
return getGmInstrument(Mt32MemoryTimbreMaps[i]);
i++;
}
+
return MIDI_UNMAPPED;
}
byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) {
int i = 0;
+ if (Mt32dynamicMappings != NULL) {
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ if (scumm_strnicmp(iname, (*it).name, 10) == 0)
+ return (*it).gmRhythmKey;
+ }
+ }
+
while (Mt32MemoryTimbreMaps[i].name) {
if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
return Mt32MemoryTimbreMaps[i].gmRhythmKey;
i++;
}
+
return MIDI_UNMAPPED;
}
@@ -801,6 +854,16 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
Resource *res = NULL;
+ if (g_sci && g_sci->_features->useAltWinGMSound()) {
+ res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0);
+ if (!(res && isMt32GmPatch(res->data, res->size))) {
+ // Don't do any mapping when a Windows alternative track is selected
+ // and no MIDI patch is available
+ _useMT32Track = false;
+ return 0;
+ }
+ }
+
if (_isMt32) {
// MT-32
resetMt32();
@@ -825,17 +888,22 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
// There is a GM patch
readMt32GmPatch(res->data, res->size);
- // Detect the format of patch 1, so that we know what play mask to use
- res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
- if (!res)
+ if (g_sci && g_sci->_features->useAltWinGMSound()) {
+ // Always use the GM track if an alternative GM Windows soundtrack is selected
_useMT32Track = false;
- else
- _useMT32Track = !isMt32GmPatch(res->data, res->size);
+ } else {
+ // Detect the format of patch 1, so that we know what play mask to use
+ res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+ if (!res)
+ _useMT32Track = false;
+ else
+ _useMT32Track = !isMt32GmPatch(res->data, res->size);
- // Check if the songs themselves have a GM track
- if (!_useMT32Track) {
- if (!resMan->isGMTrackIncluded())
- _useMT32Track = true;
+ // Check if the songs themselves have a GM track
+ if (!_useMT32Track) {
+ if (!resMan->isGMTrackIncluded())
+ _useMT32Track = true;
+ }
}
} else {
// No GM patch found, map instruments using MT-32 patch
@@ -861,10 +929,16 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
if (res) {
- if (!isMt32GmPatch(res->data, res->size))
+ if (!isMt32GmPatch(res->data, res->size)) {
mapMt32ToGm(res->data, res->size);
- else
- error("MT-32 patch has wrong type");
+ } else {
+ if (getSciVersion() <= SCI_VERSION_2_1) {
+ error("MT-32 patch has wrong type");
+ } else {
+ // Happens in the SCI3 interactive demo of Lighthouse
+ warning("TODO: Ignoring new SCI3 type of MT-32 patch for now (size = %d)", res->size);
+ }
+ }
} else {
// No MT-32 patch present, try to read from MT32.DRV
Common::File f;
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index 129159ecdc..dabe869a8f 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -79,10 +79,10 @@ enum {
class MidiPlayer : public MidiDriver {
protected:
MidiDriver *_driver;
- byte _reverb;
+ int8 _reverb;
public:
- MidiPlayer(SciVersion version) : _driver(0), _reverb(0), _version(version) { }
+ MidiPlayer(SciVersion version) : _driver(0), _reverb(-1), _version(version) { }
int open() {
ResourceManager *resMan = g_sci->getResMan(); // HACK
@@ -111,8 +111,10 @@ public:
return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0;
}
- virtual byte getReverb() const { return _reverb; }
- virtual void setReverb(byte reverb) { _reverb = reverb; }
+ // Returns the current reverb, or -1 when no reverb is active
+ int8 getReverb() const { return _reverb; }
+ // Sets the current reverb, used mainly in MT-32
+ virtual void setReverb(int8 reverb) { _reverb = reverb; }
virtual void playSwitch(bool play) {
if (!play) {
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index d53f919f8f..0b9d7617b8 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -495,13 +495,28 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0xB:
info.basic.param1 = *(_position._play_pos++);
info.basic.param2 = *(_position._play_pos++);
- if (info.channel() == 0xF) {// SCI special
- // Reference for some events:
- // http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference
- // Also, sci/sound/iterator/iterator.cpp, function BaseSongIterator::parseMidiCommand()
+
+ // Reference for some events:
+ // http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference
+ // Handle common special events
+ switch (info.basic.param1) {
+ case kSetReverb:
+ if (info.basic.param2 == 127) // Set global reverb instead
+ _pSnd->reverb = _music->getGlobalReverb();
+ else
+ _pSnd->reverb = info.basic.param2;
+
+ ((MidiPlayer *)_driver)->setReverb(_pSnd->reverb);
+ break;
+ default:
+ break;
+ }
+
+ // Handle events sent to the SCI special channel (15)
+ if (info.channel() == 0xF) {
switch (info.basic.param1) {
case kSetReverb:
- ((MidiPlayer *)_driver)->setReverb(info.basic.param2);
+ // Already handled above
break;
case kMidiHold:
// Check if the hold ID marker is the same as the hold ID
@@ -624,6 +639,21 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
}// switch (info.command())
}
+byte MidiParser_SCI::getSongReverb() {
+ assert(_track);
+
+ if (_soundVersion >= SCI_VERSION_1_EARLY) {
+ for (int i = 0; i < _track->channelCount; i++) {
+ SoundResource::Channel &channel = _track->channels[i];
+ // Peek ahead in the control channel to get the default reverb setting
+ if (channel.number == 15 && channel.size >= 7)
+ return channel.data[6];
+ }
+ }
+
+ return 127;
+}
+
void MidiParser_SCI::allNotesOff() {
if (!_driver)
return;
@@ -653,8 +683,10 @@ void MidiParser_SCI::allNotesOff() {
// support this...).
for (i = 0; i < 16; ++i) {
- if (_channelRemap[i] != -1)
+ if (_channelRemap[i] != -1) {
sendToDriver(0xB0 | i, 0x7b, 0); // All notes off
+ sendToDriver(0xB0 | i, 0x40, 0); // Also send a sustain off event (bug #3116608)
+ }
}
memset(_active_notes, 0, sizeof(_active_notes));
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index 9d0cb15e74..fa24a209e4 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -80,6 +80,7 @@ public:
void allNotesOff();
const byte *getMixedData() const { return _mixedData; }
+ byte getSongReverb();
void tryToOwnChannels();
void lostChannels();
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 0dfa02c83f..d72af2b006 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -36,10 +36,12 @@
#include "sci/sound/midiparser_sci.h"
#include "sci/sound/music.h"
+//#define DISABLE_REMAPPING
+
namespace Sci {
SciMusic::SciMusic(SciVersion soundVersion)
- : _soundVersion(soundVersion), _soundOn(true), _masterVolume(0) {
+ : _soundVersion(soundVersion), _soundOn(true), _masterVolume(0), _globalReverb(0) {
// Reserve some space in the playlist, to avoid expensive insertion
// operations
@@ -116,6 +118,8 @@ void SciMusic::init() {
// remapping).
_driverFirstChannel = _pMidiDrv->getFirstChannel();
_driverLastChannel = _pMidiDrv->getLastChannel();
+ if (getSciVersion() <= SCI_VERSION_0_LATE)
+ _globalReverb = _pMidiDrv->getReverb(); // Init global reverb for SCI0
}
void SciMusic::miditimerCallback(void *p) {
@@ -158,8 +162,10 @@ void SciMusic::sendMidiCommandsFromQueue() {
}
void SciMusic::clearPlayList() {
- Common::StackLock lock(_mutex);
-
+ // we must NOT lock our mutex here. Playlist is modified inside soundKill() which will lock the mutex
+ // during deletion. If we lock it here, a deadlock may occur within soundStop() because that one
+ // calls the mixer, which will also lock the mixer mutex and if the mixer thread is active during
+ // that time, we will get a deadlock.
while (!_playList.empty()) {
soundStop(_playList[0]);
soundKill(_playList[0]);
@@ -223,9 +229,36 @@ MusicEntry *SciMusic::getActiveSci0MusicSlot() {
return highestPrioritySlot;
}
-void SciMusic::setReverb(byte reverb) {
+void SciMusic::setGlobalReverb(int8 reverb) {
Common::StackLock lock(_mutex);
- _pMidiDrv->setReverb(reverb);
+ if (reverb != 127) {
+ // Set global reverb normally
+ _globalReverb = reverb;
+
+ // Check the reverb of the active song...
+ const MusicList::iterator end = _playList.end();
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
+ if ((*i)->status == kSoundPlaying) {
+ if ((*i)->reverb == 127) // Active song has no reverb
+ _pMidiDrv->setReverb(reverb); // Set the global reverb
+ break;
+ }
+ }
+ } else {
+ // Set reverb of the active song
+ const MusicList::iterator end = _playList.end();
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
+ if ((*i)->status == kSoundPlaying) {
+ _pMidiDrv->setReverb((*i)->reverb); // Set the song's reverb
+ break;
+ }
+ }
+ }
+}
+
+byte SciMusic::getCurrentReverb() {
+ Common::StackLock lock(_mutex);
+ return _pMidiDrv->getReverb();
}
static bool musicEntryCompare(const MusicEntry *l, const MusicEntry *r) {
@@ -286,6 +319,7 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
+ pSnd->reverb = pSnd->pMidiParser->getSongReverb();
pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
@@ -295,6 +329,10 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
// This one checks, if requested channel is available -> in that case give
// caller that channel. Otherwise look for an unused one
int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) {
+#ifdef DISABLE_REMAPPING
+ return bestChannel;
+#endif
+
// Don't even try this for SCI0
if (_soundVersion <= SCI_VERSION_0_LATE)
return bestChannel;
@@ -404,7 +442,7 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
uint16 prevLoop = pSnd->loop;
pSnd->loop = 0;
// Fast forward to the last position and perform associated events when loading
- pSnd->pMidiParser->jumpToTick(pSnd->ticker, true);
+ pSnd->pMidiParser->jumpToTick(pSnd->ticker, true, true, true);
// Restore looping
pSnd->loop = prevLoop;
}
@@ -654,6 +692,7 @@ MusicEntry::MusicEntry() {
loop = 0;
volume = MUSIC_VOLUME_DEFAULT;
hold = -1;
+ reverb = -1;
pauseCounter = 0;
sampleLoopCounter = 0;
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index 9fcbb9346d..f735fcd6c5 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -76,6 +76,7 @@ public:
uint16 loop;
int16 volume;
int16 hold;
+ int8 reverb;
int16 pauseCounter;
uint sampleLoopCounter;
@@ -187,7 +188,10 @@ public:
void sendMidiCommand(uint32 cmd);
void sendMidiCommand(MusicEntry *pSnd, uint32 cmd);
- void setReverb(byte reverb);
+ void setGlobalReverb(int8 reverb);
+ int8 getGlobalReverb() { return _globalReverb; }
+
+ byte getCurrentReverb();
virtual void saveLoadWithSerializer(Common::Serializer &ser);
@@ -218,6 +222,7 @@ private:
bool _soundOn;
byte _masterVolume;
MusicEntry *_usedChannel[16];
+ int8 _globalReverb;
MidiCommandQueue _queuedCommands;
MusicType _musicType;
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index 790164cf41..863fe6c33a 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -28,7 +28,9 @@
#include "sci/sound/music.h"
#include "sci/sound/soundcmd.h"
+#include "sci/engine/features.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/object.h"
#include "sci/engine/selector.h"
namespace Sci {
@@ -52,6 +54,9 @@ reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) {
void SoundCommandParser::processInitSound(reg_t obj) {
int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number));
+ // Modify the resourceId for the Windows versions that have an alternate MIDI soundtrack, like SSCI did.
+ if (g_sci && g_sci->_features->useAltWinGMSound())
+ resourceId += 1000;
// Check if a track with the same sound object is already playing
MusicEntry *oldSound = _music->getSlot(obj);
@@ -70,6 +75,7 @@ void SoundCommandParser::processInitSound(reg_t obj) {
newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF;
if (_soundVersion >= SCI_VERSION_1_EARLY)
newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
+ newSound->reverb = -1; // initialize to SCI invalid, it'll be set correctly in soundInitSnd() below
debugC(2, kDebugLevelSound, "kDoSound(init): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj),
resourceId, newSound->loop, newSound->priority, newSound->volume);
@@ -121,6 +127,9 @@ void SoundCommandParser::processPlaySound(reg_t obj) {
}
int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
+ // Modify the resourceId for the Windows versions that have an alternate MIDI soundtrack, like SSCI did.
+ if (g_sci && g_sci->_features->useAltWinGMSound())
+ resourceId += 1000;
if (musicSlot->resourceId != resourceId) { // another sound loaded into struct
processDisposeSound(obj);
@@ -490,10 +499,17 @@ reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) {
return acc;
}
-reg_t SoundCommandParser::kDoSoundReverb(int argc, reg_t *argv, reg_t acc) {
- debugC(2, kDebugLevelSound, "doSoundReverb: %d", argv[0].toUint16() & 0xF);
- _music->setReverb(argv[0].toUint16() & 0xF);
- return acc;
+reg_t SoundCommandParser::kDoSoundGlobalReverb(int argc, reg_t *argv, reg_t acc) {
+ byte prevReverb = _music->getCurrentReverb();
+ byte reverb = argv[0].toUint16() & 0xF;
+
+ if (argc == 1) {
+ debugC(2, kDebugLevelSound, "doSoundGlobalReverb: %d", argv[0].toUint16() & 0xF);
+ if (reverb <= 10)
+ _music->setGlobalReverb(reverb);
+ }
+
+ return make_reg(0, prevReverb);
}
reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) {
@@ -579,8 +595,13 @@ reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc)
}
if (value == -1) {
+ uint16 resourceNr = musicSlot->resourceId;
+ // Modify the resourceId for the Windows versions that have an alternate MIDI soundtrack, like SSCI did.
+ if (g_sci && g_sci->_features->useAltWinGMSound())
+ resourceNr += 1000;
+
// Set priority from the song data
- Resource *song = _resMan->findResource(ResourceId(kResourceTypeSound, musicSlot->resourceId), 0);
+ Resource *song = _resMan->findResource(ResourceId(kResourceTypeSound, resourceNr), 0);
if (song->data[0] == 0xf0)
_music->soundSetPriority(musicSlot, song->data[1]);
else
diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h
index 61371d903f..45b93e9a09 100644
--- a/engines/sci/sound/soundcmd.h
+++ b/engines/sci/sound/soundcmd.h
@@ -53,7 +53,7 @@ public:
// Functions used for game state loading
void clearPlayList();
void syncPlayList(Common::Serializer &s);
- void reconstructPlayList(int savegame_version);
+ void reconstructPlayList();
// Functions used for the ScummVM menus
void setMasterVolume(int vol);
@@ -94,7 +94,7 @@ public:
reg_t kDoSoundUpdate(int argc, reg_t *argv, reg_t acc);
reg_t kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc);
reg_t kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundReverb(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundGlobalReverb(int argc, reg_t *argv, reg_t acc);
reg_t kDoSoundSetHold(int argc, reg_t *argv, reg_t acc);
reg_t kDoSoundDummy(int argc, reg_t *argv, reg_t acc);
reg_t kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc);
diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp
index 58fd60621d..a08f837866 100644
--- a/engines/sci/video/seq_decoder.cpp
+++ b/engines/sci/video/seq_decoder.cpp
@@ -26,6 +26,7 @@
#include "common/debug.h"
#include "common/endian.h"
#include "common/archive.h"
+#include "common/stream.h"
#include "common/system.h"
#include "common/util.h"
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 1bcb065b25..ca1dc8869f 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -691,6 +691,7 @@ void Actor_v3::walkActor() {
int Actor::remapDirection(int dir, bool is_walking) {
int specdir;
byte flags;
+ byte mask;
bool flipX;
bool flipY;
@@ -769,6 +770,14 @@ int Actor::remapDirection(int dir, bool is_walking) {
case 6:
return 180;
}
+
+ // MM C64 stores flags as a part of the mask
+ if (_vm->_game.version == 0) {
+ mask = _vm->getMaskFromBox(_walkbox);
+ // face the wall if climbing/descending a ladder
+ if ((mask & 0x8C) == 0x84)
+ return 0;
+ }
}
// OR 1024 in to signal direction interpolation should be done
return normalizeAngle(dir) | 1024;
@@ -1043,9 +1052,17 @@ static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY)
// yDist must be divided by 4, as we are using 8x2 pixels
// blocks for actor coordinates).
int xDist = ABS(x - destX);
- int yDist = ABS(y - destY) / 4;
+ int yDist;
int dist;
+ // MM C64: This fixes the trunk bug (#3070065), as well
+ // as the fruit bowl, however im not sure if its
+ // the proper solution or not.
+ if( g_scumm->_game.version == 0 )
+ yDist = ABS(y - destY);
+ else
+ yDist = ABS(y - destY) / 4;
+
if (xDist < yDist)
dist = (xDist >> 1) + yDist;
else
@@ -1073,6 +1090,7 @@ AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) {
abr.x = foundX;
abr.y = foundY;
abr.box = box;
+
break;
}
if (dist < bestDist) {
@@ -2259,7 +2277,7 @@ void Actor::setActorCostume(int c) {
}
}
-static const char* v0ActorNames[0x19] = {
+static const char* v0ActorNames_English[25] = {
"Syd",
"Razor",
"Dave",
@@ -2275,10 +2293,36 @@ static const char* v0ActorNames[0x19] = {
"Purple Tentacle",
"Green Tentacle",
"Meteor",
+ "",
+ "",
+ "",
"Plant",
"",
"",
"",
+ "Sandy"
+};
+
+static const char* v0ActorNames_German[25] = {
+ "Syd",
+ "Razor",
+ "Dave",
+ "Michael",
+ "Bernard",
+ "Wendy",
+ "Jeff",
+ "",
+ "Dr.Fred",
+ "Schwester Edna",
+ "Weird Ed",
+ "Ted",
+ "Lila Tentakel",
+ "Gr<nes Tentakel",
+ "Meteor",
+ "",
+ "",
+ "",
+ "Pflanze",
"",
"",
"",
@@ -2289,8 +2333,15 @@ const byte *Actor::getActorName() {
const byte *ptr = NULL;
if (_vm->_game.version == 0) {
- if (_number)
- ptr = (const byte *)v0ActorNames[_number - 1];
+ if (_number) {
+ switch (_vm->_language) {
+ case Common::DE_DEU:
+ ptr = (const byte *)v0ActorNames_German[_number - 1];
+ break;
+ default:
+ ptr = (const byte *)v0ActorNames_English[_number - 1];
+ }
+ }
} else {
ptr = _vm->getResourceAddress(rtActorName, _number);
}
@@ -2561,6 +2612,21 @@ void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) {
#endif
+void ActorC64::saveLoadWithSerializer(Serializer *ser) {
+ Actor::saveLoadWithSerializer(ser);
+
+ static const SaveLoadEntry actorEntries[] = {
+ MKLINE(ActorC64, _costCommand, sleByte, VER(84)),
+ MKLINE(ActorC64, _costFrame, sleByte, VER(84)),
+ MKLINE(ActorC64, _miscflags, sleByte, VER(84)),
+ MKLINE(ActorC64, _speaking, sleByte, VER(84)),
+ MKLINE(ActorC64, _speakingPrev, sleByte, VER(84)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, actorEntries);
+}
+
void Actor::saveLoadWithSerializer(Serializer *ser) {
static const SaveLoadEntry actorEntries[] = {
MKLINE(Actor, _pos.x, sleInt16, VER(8)),
diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h
index 88ba9902b4..98854ec5ba 100644
--- a/engines/scumm/actor.h
+++ b/engines/scumm/actor.h
@@ -305,7 +305,7 @@ public:
void classChanged(int cls, bool value);
// Used by the save/load system:
- void saveLoadWithSerializer(Serializer *ser);
+ virtual void saveLoadWithSerializer(Serializer *ser);
protected:
bool isInClass(int cls);
@@ -381,14 +381,16 @@ protected:
class ActorC64 : public Actor_v2 {
public:
- // FIXME: These vars are never saved, which might lead to broken save states.
- byte _miscflags;
- byte _speaking, _speakingPrev;
byte _costCommand, _costFrame;
+ byte _miscflags; // 0x1: strong, 0x8: Ed's enemy, 0x40: stop moving, 0x80: hide(dead/radiation suit)
+ byte _speaking, _speakingPrev;
public:
ActorC64(ScummEngine *scumm, int id) : Actor_v2(scumm, id) {
- _speaking = _speakingPrev = _costCommand = _costFrame = 0;
+ _costCommand = 0;
+ _costFrame = 0;
+ _speaking = 0;
+ _speakingPrev = 0;
}
virtual void initActor(int mode) {
Actor_v2::initActor(mode);
@@ -397,6 +399,9 @@ public:
}
}
+ // Used by the save/load system:
+ virtual void saveLoadWithSerializer(Serializer *ser);
+
protected:
};
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index d5d6b6182b..354a1d4491 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -1087,10 +1087,10 @@ void AkosRenderer::akos16SetupBitReader(const byte *src) {
}
#define AKOS16_FILL_BITS() \
- if (_akos16.numbits <= 8) { \
- _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \
- _akos16.numbits += 8; \
- }
+ if (_akos16.numbits <= 8) { \
+ _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \
+ _akos16.numbits += 8; \
+ }
#define AKOS16_EAT_BITS(n) \
_akos16.numbits -= (n); \
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
index dc6f10696f..15d5f04ed5 100644
--- a/engines/scumm/boxes.cpp
+++ b/engines/scumm/boxes.cpp
@@ -614,10 +614,8 @@ BoxCoords ScummEngine::getBoxCoordinates(int boxnum) {
box->lr.x = bp->c64.x2;
box->lr.y = bp->c64.y2;
- if (bp->c64.mask & 0x88) {
+ if ((bp->c64.mask & 0x88) == 0x88) {
// walkbox for (right/left) corner
- // TODO: ladders (incl. man-eating plant) have mask 0x8A,
- // must those walkboxes be adjusted?
if (bp->c64.mask & 0x04)
box->ur = box->ul;
else
@@ -923,32 +921,32 @@ bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, Common::Point
static void printMatrix(byte *boxm, int num) {
int i;
for (i = 0; i < num; i++) {
- printf("%d: ", i);
+ debugN("%d: ", i);
while (*boxm != 0xFF) {
- printf("%d, ", *boxm);
+ debug("%d, ", *boxm);
boxm++;
}
boxm++;
- printf("\n");
+ debug("\n");
}
}
static void printMatrix2(byte *matrix, int num) {
int i, j;
- printf(" ");
+ debug(" ");
for (i = 0; i < num; i++)
- printf("%2d ", i);
- printf("\n");
+ debug("%2d ", i);
+ debug("\n");
for (i = 0; i < num; i++) {
- printf("%2d: ", i);
+ debug("%2d: ", i);
for (j = 0; j < num; j++) {
int val = matrix[i * num + j];
if (val == Actor::kInvalidBox)
- printf(" ? ");
+ debug(" ? ");
else
- printf("%2d ", val);
+ debug("%2d ", val);
}
- printf("\n");
+ debug("\n");
}
}
#endif
@@ -1055,9 +1053,9 @@ void ScummEngine::createBoxMatrix() {
#if BOX_DEBUG
- printf("Itinerary matrix:\n");
+ debug("Itinerary matrix:\n");
printMatrix2(itineraryMatrix, num);
- printf("compressed matrix:\n");
+ debug("compressed matrix:\n");
printMatrix(getBoxMatrixBaseAddr(), num);
#endif
diff --git a/engines/scumm/charset-fontdata.cpp b/engines/scumm/charset-fontdata.cpp
index 904e40d9ea..378e8e9d8d 100644
--- a/engines/scumm/charset-fontdata.cpp
+++ b/engines/scumm/charset-fontdata.cpp
@@ -589,20 +589,20 @@ CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language)
b = data[offset+3];
len = data[offset+4];
while (len--) {
- printf("0x%02x, ", b);
+ debugN("0x%02x, ", b);
count++;
if (count % 8 == 0)
- printf("\n");
+ debugN("\n");
}
offset += 6;
} else {
- printf("0x%02x, ", data[offset]);
+ debugN("0x%02x, ", data[offset]);
count++;
if (count % 8 == 0)
- printf("\n");
+ debugN("\n");
}
}
- printf("\n");
+ debugN("\n");
_vm->_system->quit();
#endif
}
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 5b33fee742..8d7f9c9e9f 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -55,20 +55,12 @@ void ScummEngine::loadCJKFont() {
#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE
error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build");
#endif
- int numChar = 256 * 32;
- _2byteWidth = 16;
- _2byteHeight = 16;
- // use FM-TOWNS font rom, since game files don't have kanji font resources
- if (!fp.open("fmt_fnt.rom")) {
- error("SCUMM::Font: Couldn't open fmt_fnt.rom");
- } else {
- _useCJKMode = true;
- debug(2, "Loading FM-TOWNS Kanji rom");
- _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar];
- fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar);
- fp.close();
- }
+ // use FM-TOWNS font rom, since game files don't have kanji font resources
+ _cjkFont = Graphics::FontSJIS::createFont(Common::kPlatformFMTowns);
+ if (!_cjkFont)
+ error("SCUMM::Font: Couldn't open file 'FMT_FNT.ROM'");
_textSurfaceMultiplier = 2;
+ _useCJKMode = true;
} else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) {
int numChar = 3418;
_2byteWidth = 12;
@@ -170,90 +162,6 @@ void ScummEngine::loadCJKFont() {
}
}
-static int SJIStoFMTChunk(int f, int s) { //converts sjis code to fmt font offset
- enum {
- KANA = 0,
- KANJI = 1,
- EKANJI = 2
- };
- int base = s - ((s + 1) % 32);
- int c = 0, p = 0, chunk_f = 0, chunk = 0, cr = 0, kanjiType = KANA;
-
- if (f >= 0x81 && f <= 0x84) kanjiType = KANA;
- if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI;
- if (f >= 0xe0 && f <= 0xea) kanjiType = EKANJI;
-
- if ((f > 0xe8 || (f == 0xe8 && base >= 0x9f)) || (f > 0x90 || (f == 0x90 && base >= 0x9f))) {
- c = 48; //correction
- p = -8; //correction
- }
-
- if (kanjiType == KANA) {//Kana
- chunk_f = (f - 0x81) * 2;
- } else if (kanjiType == KANJI) {//Standard Kanji
- p += f - 0x88;
- chunk_f = c + 2 * p;
- } else if (kanjiType == EKANJI) {//Enhanced Kanji
- p += f - 0xe0;
- chunk_f = c + 2 * p;
- }
-
- // Base corrections
- if (base == 0x7f && s == 0x7f)
- base -= 0x20;
- if (base == 0x9f && s == 0xbe)
- base += 0x20;
- if (base == 0xbf && s == 0xde)
- base += 0x20;
- //if (base == 0x7f && s == 0x9e)
- // base += 0x20;
-
- switch (base) {
- case 0x3f:
- cr = 0; //3f
- if (kanjiType == KANA) chunk = 1;
- else if (kanjiType == KANJI) chunk = 31;
- else if (kanjiType == EKANJI) chunk = 111;
- break;
- case 0x5f:
- cr = 0; //5f
- if (kanjiType == KANA) chunk = 17;
- else if (kanjiType == KANJI) chunk = 47;
- else if (kanjiType == EKANJI) chunk = 127;
- break;
- case 0x7f:
- cr = -1; //80
- if (kanjiType == KANA) chunk = 9;
- else if (kanjiType == KANJI) chunk = 63;
- else if (kanjiType == EKANJI) chunk = 143;
- break;
- case 0x9f:
- cr = 1; //9e
- if (kanjiType == KANA) chunk = 2;
- else if (kanjiType == KANJI) chunk = 32;
- else if (kanjiType == EKANJI) chunk = 112;
- break;
- case 0xbf:
- cr = 1; //be
- if (kanjiType == KANA) chunk = 18;
- else if (kanjiType == KANJI) chunk = 48;
- else if (kanjiType == EKANJI) chunk = 128;
- break;
- case 0xdf:
- cr = 1; //de
- if (kanjiType == KANA) chunk = 10;
- else if (kanjiType == KANJI) chunk = 64;
- else if (kanjiType == EKANJI) chunk = 144;
- break;
- default:
- debug(4, "Invalid Char! f %x s %x base %x c %d p %d", f, s, base, c, p);
- return 0;
- }
-
- debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr);
- return ((chunk_f + chunk) * 32 + (s - base)) + cr;
-}
-
static int SJIStoPCEChunk(int f, int s) { //converts sjis code to pce font offset
// rangeTbl maps SJIS char-codes to the PCE System Card font rom.
// Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range.
@@ -330,9 +238,8 @@ byte *ScummEngine::get2byteCharPtr(int idx) {
}
idx = (SWAP_CONSTANT_16(idx) & 0x7fff) - 1;
- } else {
- idx = SJIStoFMTChunk((idx % 256), (idx / 256));
}
+
break;
case Common::ZH_TWN:
{
@@ -458,11 +365,11 @@ void CharsetRendererV3::setCurID(int32 id) {
int CharsetRendererCommon::getFontHeight() {
if (_vm->_useCJKMode) {
if (_vm->_game.platform == Common::kPlatformFMTowns) {
- static const uint8 sjisFontHeightM1[] = { 0, 9, 10, 9, 10, 9, 10, 0, 0 };
- static const uint8 sjisFontHeightM2[] = { 8, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
- static const uint8 sjisFontHeightI4[] = { 8, 8, 9, 9, 9, 8, 8, 8, 8, 8 };
+ static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 };
+ static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
+ static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 };
const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2);
- return htbl[_curId];
+ return (_vm->_game.version == 3) ? 8 : htbl[_curId];
} else {
return MAX(_vm->_2byteHeight + 1, _fontHeight);
}
@@ -474,26 +381,29 @@ int CharsetRendererCommon::getFontHeight() {
int CharsetRendererClassic::getCharWidth(uint16 chr) {
int spacing = 0;
- if (_vm->_game.platform == Common::kPlatformFMTowns) {
- if (_vm->_useCJKMode) {
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
if ((chr & 0xff00) == 0xfd00) {
chr &= 0xff;
} else if (chr >= 256) {
- spacing = 9;
- } else if (chr >= 128) {
- spacing = 5;
+ spacing = 8;
+ } else if (useTownsFontRomCharacter(chr)) {
+ spacing = 4;
}
if (spacing) {
- static const uint8 sjisWidthM1[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
- static const uint8 sjisWidthM2[] = { 0, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
- static const uint8 sjisWidthI4[] = { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 };
- const uint8 *wtbl = (_vm->_game.id == GID_MONKEY) ? sjisWidthM1 : ((_vm->_game.id == GID_INDY4) ? sjisWidthI4 : sjisWidthM2);
- spacing += wtbl[_curId];
+ if (_vm->_game.id == GID_MONKEY) {
+ spacing++;
+ if (_curId == 2)
+ spacing++;
+ } else if (_vm->_game.id != GID_INDY4 && _curId == 1) {
+ spacing++;
+ }
}
+
+ } else if (chr >= 0x80) {
+ return _vm->_2byteWidth / 2;
}
- } else if (chr >= 0x80 && _vm->_useCJKMode) {
- return _vm->_2byteWidth / 2;
}
if (!spacing) {
@@ -506,10 +416,26 @@ int CharsetRendererClassic::getCharWidth(uint16 chr) {
return spacing;
}
+bool CharsetRendererClassic::useTownsFontRomCharacter(uint16 chr) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_vm->_game.platform != Common::kPlatformFMTowns || !_vm->_useCJKMode)
+ return false;
+
+ if (chr < 128) {
+ if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127))
+ return true;
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
int CharsetRenderer::getStringWidth(int arg, const byte *text) {
int pos = 0;
int width = 1;
- uint16 chr;
+ int chr;
int oldID = getCurID();
int code = (_vm->_game.heversion >= 80) ? 127 : 64;
@@ -570,8 +496,10 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
if (_vm->_useCJKMode) {
if (_vm->_game.platform == Common::kPlatformFMTowns) {
- if ((chr >= 0x80 && chr <= 0x9f) || (chr >= 0xe0 && chr <= 0xfd))
- chr = (chr << 8) | text[pos++];
+ if (checkSJISCode(chr))
+ // This strange character conversion is the exact way the original does it here.
+ // This is the only way to get an accurate text formatting in the MI1 intro.
+ chr = (int8)text[pos++] | (chr << 8);
} else if (chr & 0x80) {
pos++;
width += _vm->_2byteWidth;
@@ -589,7 +517,7 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
int lastspace = -1;
int curw = 1;
- byte chr;
+ int chr;
int oldID = getCurID();
int code = (_vm->_game.heversion >= 80) ? 127 : 64;
@@ -651,9 +579,17 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
if (chr == _vm->_newLineCharacter)
lastspace = pos - 1;
- if ((chr & 0x80) && _vm->_useCJKMode) {
- pos++;
- curw += _vm->_2byteWidth;
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if (checkSJISCode(chr))
+ // This strange character conversion is the exact way the original does it here.
+ // This is the only way to get an accurate text formatting in the MI1 intro.
+ chr = (int8)str[pos++] | (chr << 8);
+ curw += getCharWidth(chr);
+ } else if (chr & 0x80) {
+ pos++;
+ curw += _vm->_2byteWidth;
+ }
} else {
curw += getCharWidth(chr);
}
@@ -671,11 +607,21 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
}
int CharsetRendererV3::getCharWidth(uint16 chr) {
- if (chr & 0x80 && _vm->_useCJKMode)
- return _vm->_2byteWidth / 2;
int spacing = 0;
- spacing = *(_widthTable + chr);
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if (chr >= 256)
+ spacing = 8;
+ else if (chr >= 128)
+ spacing = 4;
+ } else if (chr & 0x80) {
+ spacing = _vm->_2byteWidth / 2;
+ }
+ }
+
+ if (!spacing)
+ spacing = *(_widthTable + chr);
return spacing;
}
@@ -719,11 +665,23 @@ void CharsetRendererPCE::setColor(byte color) {
void CharsetRendererCommon::enableShadow(bool enable) {
if (enable) {
if (_vm->_game.platform == Common::kPlatformFMTowns) {
- _shadowColor =
+ _shadowColor = 8;
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
-#else
- 8;
+ _shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
+ if (_vm->_cjkFont) {
+ if (_vm->_game.version == 5) {
+ if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) ||
+ ((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) ||
+ ((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode);
+ } else {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+ }
+ _vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3);
+ } else {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode);
+ }
+ }
#endif
_shadowMode = kFMTOWNSShadowMode;
} else {
@@ -731,6 +689,10 @@ void CharsetRendererCommon::enableShadow(bool enable) {
_shadowMode = kNormalShadowMode;
}
} else {
+ if (_vm->_cjkFont) {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+ _vm->_cjkFont->toggleFlippedMode(false);
+ }
_shadowMode = kNoShadowMode;
}
}
@@ -747,7 +709,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
VirtScreen *vs;
const byte *charPtr;
byte *dst;
- int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
+ int is2byte = (chr >= 256 && _vm->_useCJKMode) ? 1 : 0;
assertRange(0, _curId, _vm->_numCharsets - 1, "charset");
@@ -757,10 +719,16 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
if (chr == '@')
return;
- if (is2byte) {
- charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
+ if (_vm->_useCJKMode && chr > 127) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ charPtr = 0;
+ width = _vm->_cjkFont->getCharWidth(chr);
+ height = _vm->_cjkFont->getFontHeight();
+ } else {
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ charPtr = _vm->get2byteCharPtr(chr);
+ }
} else {
charPtr = _fontPtr + chr * 8;
width = getCharWidth(chr);
@@ -775,8 +743,8 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
origHeight = height;
if (_shadowMode != kNoShadowMode) {
- width += _vm->_textSurfaceMultiplier;
- height += _vm->_textSurfaceMultiplier;
+ width++;
+ height++;
}
if (_firstChar) {
@@ -798,20 +766,28 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
if (
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- (_vm->_game.platform != Common::kPlatformFMTowns || (_vm->_game.id == GID_LOOM && !is2byte)) &&
-#endif
+ (_vm->_game.platform != Common::kPlatformFMTowns) &&
+#endif
(ignoreCharsetMask || !vs->hasTwoBuffers)) {
dst = vs->getPixels(_left, drawTop);
- drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
+ if (charPtr)
+ drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
+ else if (_vm->_cjkFont)
+ _vm->_cjkFont->drawChar(vs, chr, _left, drawTop, _color, _shadowColor);
} else {
dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier);
- drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.bytesPerPixel);
+ if (charPtr)
+ drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.bytesPerPixel, (_vm->_textSurfaceMultiplier == 2 && !is2byte));
+ else if (_vm->_cjkFont)
+ _vm->_cjkFont->drawChar(_vm->_textSurface, chr, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, _color, _shadowColor);
+ if (is2byte)
+ origWidth /= _vm->_textSurfaceMultiplier;
}
if (_str.left > _left)
_str.left = _left;
- _left += origWidth / _vm->_textSurfaceMultiplier;
+ _left += origWidth;
if (_str.right < _left) {
_str.right = _left;
@@ -823,15 +799,20 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
_str.bottom = _top + height / _vm->_textSurfaceMultiplier;
}
-void CharsetRendererV3::drawChar(int chr, const Graphics::Surface &s, int x, int y) {
+void CharsetRendererV3::drawChar(int chr, Graphics::Surface &s, int x, int y) {
const byte *charPtr;
byte *dst;
int width, height;
int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
if (is2byte) {
- charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ _vm->_cjkFont->drawChar(s, chr, x * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, _color, _shadowColor);
+ return;
+ } else {
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ }
} else {
charPtr = _fontPtr + chr * 8;
// width = height = 8;
@@ -901,7 +882,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
int offsX, offsY;
VirtScreen *vs;
const byte *charPtr;
- bool is2byte = (chr >= 0x80 && _vm->_useCJKMode);
+ bool is2byte = (chr >= 256 && _vm->_useCJKMode);
assertRange(1, _curId, _vm->_numCharsets - 1, "charset");
@@ -917,14 +898,42 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
processTownsCharsetColors(_bytesPerPixel);
-#endif
+ bool noSjis = false;
- if (is2byte) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) {
+ if ((chr & 0x00ff) == 0x00fd) {
+ chr >>= 8;
+ noSjis = true;
+ }
+ }
+
+ if (useTownsFontRomCharacter(chr) && !noSjis) {
+ charPtr = 0;
+ _vm->_cjkChar = chr;
+ enableShadow(true);
+
+ width = getCharWidth(chr);
+ // For whatever reason MI1 uses a different font width
+ // for alignment calculation and for drawing when
+ // charset 2 is active. This fixes some subtle glitches.
+ if (_vm->_game.id == GID_MONKEY && _curId == 2)
+ width--;
+ origWidth = width;
+
+ origHeight = height = getFontHeight();
+ offsX = offsY = 0;
+ } else
+#endif
+ if (_vm->_useCJKMode && (chr >= 128) && !noSjis) {
enableShadow(true);
+ origWidth = width = _vm->_2byteWidth;
+ origHeight = height = _vm->_2byteHeight;
charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
offsX = offsY = 0;
+ if (_shadowMode != kNoShadowMode) {
+ width++;
+ height++;
+ }
} else {
uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
assert(charOffs < 0x14000);
@@ -932,9 +941,9 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
return;
charPtr = _fontPtr + charOffs;
- width = charPtr[0];
- height = charPtr[1];
-
+ width = origWidth = charPtr[0];
+ height = origHeight = charPtr[1];
+
if (_disableOffsX) {
offsX = 0;
} else {
@@ -945,13 +954,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
charPtr += 4; // Skip over char header
}
- origWidth = width;
- origHeight = height;
- if (_shadowMode != kNoShadowMode) {
- width += _vm->_textSurfaceMultiplier;
- height += _vm->_textSurfaceMultiplier;
- }
if (_firstChar) {
_str.left = 0;
_str.top = 0;
@@ -962,8 +965,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
_top += offsY;
_left += offsX;
- if (_left + origWidth / _vm->_textSurfaceMultiplier > _right + 1 || _left < 0) {
- _left += origWidth / _vm->_textSurfaceMultiplier;
+ if (_left + origWidth > _right + 1 || _left < 0) {
+ _left += origWidth;
_top -= offsY;
return;
}
@@ -1001,16 +1004,16 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
printCharIntern(is2byte, charPtr, origWidth, origHeight, width, height, vs, ignoreCharsetMask);
- _left += origWidth / _vm->_textSurfaceMultiplier;
+ _left += origWidth;
if (_str.right < _left) {
_str.right = _left;
- if (_shadowMode != kNoShadowMode)
+ if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode != kNoShadowMode)
_str.right++;
}
- if (_str.bottom < _top + height / _vm->_textSurfaceMultiplier)
- _str.bottom = _top + height / _vm->_textSurfaceMultiplier;
+ if (_str.bottom < _top + origHeight)
+ _str.bottom = _top + origHeight;
_top -= offsY;
}
@@ -1073,10 +1076,12 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
drawTop = _top - _vm->_screenTop;
}
- if (is2byte) {
+ if (!charPtr && _vm->_cjkFont) {
+ _vm->_cjkFont->drawChar(dstSurface, _vm->_cjkChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor);
+ } else if (is2byte) {
drawBits1(dstSurface, dstPtr, charPtr, drawTop, origWidth, origHeight, dstSurface.bytesPerPixel);
} else {
- drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight);
+ drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight, _vm->_textSurfaceMultiplier == 2);
}
if (_blitAlso && vs->hasTwoBuffers) {
@@ -1116,7 +1121,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
}
}
-void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x, int y) {
+void CharsetRendererClassic::drawChar(int chr, Graphics::Surface &s, int x, int y) {
const byte *charPtr;
byte *dst;
int width, height;
@@ -1124,9 +1129,14 @@ void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x
if (is2byte) {
enableShadow(true);
- charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ _vm->_cjkFont->drawChar(s, chr, x * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, _color, _shadowColor);
+ return;
+ } else {
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ }
} else {
uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
assert(charOffs < 0x10000);
@@ -1149,19 +1159,33 @@ void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x
}
}
-void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) {
+void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height,
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ bool scale2x) {
+#else
+ bool) {
+#endif
+
int y, x;
int color;
byte numbits, bits;
+ byte *dst2 = dst;
+ int pitch = s.pitch - width;
+
assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
bits = *src++;
numbits = 8;
- byte *cmap =
+ byte *cmap = _vm->_charsetColorMap;
+
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- (_vm->_game.platform == Common::kPlatformFMTowns) ? _vm->_townsCharsetColorMap :
+ if (_vm->_game.platform == Common::kPlatformFMTowns)
+ cmap = _vm->_townsCharsetColorMap;
+ if (scale2x) {
+ dst2 += s.pitch;
+ pitch <<= 1;
+ }
#endif
- _vm->_charsetColorMap;
for (y = 0; y < height && y + drawTop < s.h; y++) {
for (x = 0; x < width; x++) {
@@ -1169,8 +1193,21 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
if (color && y + drawTop >= 0) {
*dst = cmap[color];
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x)
+ dst[1] = dst2[0] = dst2[1] = dst[0];
+#endif
}
dst++;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x) {
+ dst++;
+ dst2 += 2;
+ }
+#endif
+
bits <<= bpp;
numbits -= bpp;
if (numbits == 0) {
@@ -1178,18 +1215,37 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
numbits = 8;
}
}
- dst += s.pitch - width;
+ dst += pitch;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ dst2 += pitch;
+#endif
}
}
-void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth,
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ bool scale2x) {
+#else
+ bool) {
+#endif
+
int y, x;
byte bits = 0;
- uint8 col =
+ uint8 col = _color;
+ int pitch = s.pitch - width * bitDepth;
+ byte *dst2 = dst + s.pitch;
+
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) ? _vm->_townsCharsetColorMap[1] :
-#endif
- _color;
+ byte *dst3 = dst2;
+ byte *dst4 = dst2;
+ if (scale2x) {
+ dst3 = dst2 + s.pitch;
+ dst4 = dst3 + s.pitch;
+ pitch <<= 1;
+ }
+ if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5)
+ col = _vm->_townsCharsetColorMap[1];
+#endif
for (y = 0; y < height && y + drawTop < s.h; y++) {
for (x = 0; x < width; x++) {
@@ -1206,23 +1262,49 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con
WRITE_UINT16(dst, _vm->_16BitPalette[_color]);
} else {
if (_shadowMode != kNoShadowMode) {
- *(dst + 1) = _shadowColor;
- *(dst + s.pitch) = _shadowColor;
- if (_shadowMode != kFMTOWNSShadowMode)
- *(dst + s.pitch + 1) = _shadowColor;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x) {
+ dst[2] = dst[3] = dst2[2] = dst2[3] = _shadowColor;
+ dst3[0] = dst4[0] = dst3[1] = dst4[1] = _shadowColor;
+ } else
+#endif
+ {
+ dst[1] = dst2[0] = _shadowColor;
+ if (_shadowMode != kFMTOWNSShadowMode)
+ dst2[1] = _shadowColor;
+ }
}
- *dst = col;
+ dst[0] = col;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x)
+ dst[1] = dst2[0] = dst2[1] = col;
+#endif
}
}
dst += bitDepth;
+ dst2 += bitDepth;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x) {
+ dst++;
+ dst2++;
+ dst3 += 2;
+ dst4 += 2;
+ }
+#endif
}
- dst += s.pitch - width * bitDepth;
+ dst += pitch;
+ dst2 += pitch;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ dst3 += pitch;
+ dst4 += pitch;
+#endif
}
}
#ifdef USE_RGB_COLOR
-void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) {
int y, x;
int bitCount = 0;
byte bits = 0;
@@ -1437,7 +1519,7 @@ void CharsetRendererNES::printChar(int chr, bool ignoreCharsetMask) {
_str.bottom = _top + height;
}
-void CharsetRendererNES::drawChar(int chr, const Graphics::Surface &s, int x, int y) {
+void CharsetRendererNES::drawChar(int chr, Graphics::Surface &s, int x, int y) {
byte *charPtr, *dst;
int width, height;
@@ -1452,7 +1534,7 @@ void CharsetRendererNES::drawChar(int chr, const Graphics::Surface &s, int x, in
drawBits1(s, dst, charPtr, y, width, height, s.bytesPerPixel);
}
-void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) {
for (int i = 0; i < 8; i++) {
byte c0 = src[i];
byte c1 = src[i + 8];
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index b5fc7b1b15..4b2e053c6d 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -27,6 +27,7 @@
#include "common/scummsys.h"
#include "common/rect.h"
+#include "graphics/sjis.h"
#include "scumm/gfx.h"
#include "scumm/saveload.h"
@@ -37,9 +38,9 @@ class NutRenderer;
struct VirtScreen;
static inline bool checkSJISCode(byte c) {
- if ((c > 0x84 && c < 0x88) || (c > 0x9f && c < 0xe0) || (c > 0xea /* && c <= 0xff */))
- return false;
- return true;
+ if ((c >= 0x80 && c <= 0x9f) || (c >= 0xe0 && c <= 0xfd))
+ return true;
+ return false;
}
@@ -74,7 +75,7 @@ public:
virtual ~CharsetRenderer();
virtual void printChar(int chr, bool ignoreCharsetMask) = 0;
- virtual void drawChar(int chr, const Graphics::Surface &s, int x, int y) {}
+ virtual void drawChar(int chr, Graphics::Surface &s, int x, int y) {}
int getStringWidth(int a, const byte *str);
void addLinebreaks(int a, byte *str, int pos, int maxwidth);
@@ -112,7 +113,8 @@ protected:
ShadowMode _shadowMode;
void enableShadow(bool enable);
- virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+ virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
+
public:
CharsetRendererCommon(ScummEngine *vm);
@@ -124,7 +126,7 @@ public:
class CharsetRendererClassic : public CharsetRendererCommon {
protected:
- void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height);
+ void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, bool scale2x = false);
void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask);
@@ -132,23 +134,28 @@ public:
CharsetRendererClassic(ScummEngine *vm) : CharsetRendererCommon(vm) {}
void printChar(int chr, bool ignoreCharsetMask);
- void drawChar(int chr, const Graphics::Surface &s, int x, int y);
+ void drawChar(int chr, Graphics::Surface &s, int x, int y);
int getCharWidth(uint16 chr);
+
+ // Some SCUMM 5 games contain hard coded logic to determine whether to use
+ // the SCUMM fonts or the FM-Towns font rom to draw a character. For the other
+ // games we will simply check for a character greater 127.
+ bool useTownsFontRomCharacter(uint16 chr);
};
class CharsetRendererNES : public CharsetRendererCommon {
protected:
byte *_trTable;
- void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+ void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
public:
CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {}
void setCurID(int32 id) {}
void printChar(int chr, bool ignoreCharsetMask);
- void drawChar(int chr, const Graphics::Surface &s, int x, int y);
+ void drawChar(int chr, Graphics::Surface &s, int x, int y);
int getFontHeight() { return 8; }
int getCharWidth(uint16 chr) { return 8; }
@@ -162,7 +169,7 @@ public:
CharsetRendererV3(ScummEngine *vm) : CharsetRendererCommon(vm) {}
void printChar(int chr, bool ignoreCharsetMask);
- void drawChar(int chr, const Graphics::Surface &s, int x, int y);
+ void drawChar(int chr, Graphics::Surface &s, int x, int y);
void setCurID(int32 id);
void setColor(byte color);
int getCharWidth(uint16 chr);
@@ -171,7 +178,7 @@ public:
#ifdef USE_RGB_COLOR
class CharsetRendererPCE : public CharsetRendererV3 {
protected:
- void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+ void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
public:
CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm) {}
diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp
index d0d8eb2240..cd366fcfd4 100644
--- a/engines/scumm/costume.cpp
+++ b/engines/scumm/costume.cpp
@@ -648,7 +648,7 @@ void ClassicCostumeRenderer::procPCEngine(Codec1 &v1) {
}
}
if (index != 128) {
- printf("%d\n", index);
+ warning("ClassicCostumeRenderer::procPCEngine: index %d != 128\n", index);
}
for (int row = 0; row < 16; ++row) {
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index b5a4070f0b..a348d9c942 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -35,7 +35,6 @@
#include "scumm/debugger.h"
#include "scumm/imuse/imuse.h"
#include "scumm/object.h"
-#include "scumm/player_v2.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
@@ -87,7 +86,7 @@ ScummDebugger::ScummDebugger(ScummEngine *s)
if (_vm->_game.id == GID_LOOM)
DCmd_Register("drafts", WRAP_METHOD(ScummDebugger, Cmd_PrintDraft));
- if (_vm->_game.id == GID_MONKEY && Common::kPlatformSegaCD)
+ if (_vm->_game.id == GID_MONKEY && _vm->_game.platform == Common::kPlatformSegaCD)
DCmd_Register("passcode", WRAP_METHOD(ScummDebugger, Cmd_Passcode));
DCmd_Register("loadgame", WRAP_METHOD(ScummDebugger, Cmd_LoadGame));
@@ -529,7 +528,7 @@ bool ScummDebugger::Cmd_Debug(int argc, const char **argv) {
} else {
DebugPrintf("Usage: debug [+CHANNEL|-CHANNEL]\n");
DebugPrintf("Enables or disables the given debug channel.\n");
- DebugPrintf("When used without parameters, lists all avaiable debug channels and their status.\n");
+ DebugPrintf("When used without parameters, lists all available debug channels and their status.\n");
}
return true;
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 9010cb84c3..6430ab7c4d 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -422,7 +422,6 @@ static void composeFileHashMap(const Common::FSList &fslist, DescMap &fileMD5Map
static void detectGames(const Common::FSList &fslist, Common::List<DetectorResult> &results, const char *gameid) {
DescMap fileMD5Map;
DetectorResult dr;
- char md5str[32+1];
// Dive one level down since mac indy3/loom has its files split into directories. See Bug #1438631
composeFileHashMap(fslist, fileMD5Map, 2, directoryGlobs);
@@ -467,7 +466,7 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
//
DetectorDesc &d = fileMD5Map[file];
if (d.md5.empty()) {
- Common::File *tmp = 0;
+ Common::SeekableReadStream *tmp = 0;
bool isDiskImg = (file.hasSuffix(".d64") || file.hasSuffix(".dsk") || file.hasSuffix(".prg"));
if (isDiskImg) {
@@ -475,14 +474,16 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
debug(2, "Falling back to disk-based detection");
} else {
- tmp = new Common::File;
- tmp->open(d.node);
+ tmp = d.node.createReadStream();
}
- if (tmp && tmp->isOpen() && Common::md5_file_string(*tmp, md5str, kMD5FileSizeLimit)) {
+ Common::String md5str;
+ if (tmp)
+ md5str = computeStreamMD5AsString(*tmp, kMD5FileSizeLimit);
+ if (!md5str.empty()) {
d.md5 = md5str;
- d.md5Entry = findInMD5Table(md5str);
+ d.md5Entry = findInMD5Table(md5str.c_str());
dr.md5 = d.md5;
@@ -494,7 +495,7 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
int filesize = tmp->size();
if (d.md5Entry->filesize != filesize)
debug(1, "SCUMM detector found matching file '%s' with MD5 %s, size %d\n",
- file.c_str(), md5str, filesize);
+ file.c_str(), md5str.c_str(), filesize);
// Sanity check: We *should* have found a matching gameid / variant at this point.
// If not, then there's a bug in our data tables...
@@ -1198,12 +1199,7 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int
int minutes = infos.time & 0xFF;
desc.setSaveTime(hour, minutes);
-
- minutes = infos.playtime / 60;
- hour = minutes / 60;
- minutes %= 60;
-
- desc.setPlayTime(hour, minutes);
+ desc.setPlayTime(infos.playtime * 1000);
}
return desc;
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 4428185774..a5542ca868 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -370,7 +370,7 @@ static const GameSettings gameVariantsTable[] = {
#ifdef USE_RGB_COLOR
// Added 16bit color
{"arttime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
- {"baseball2001", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
+ {"baseball2001", 0, 0, GID_BASEBALL2001, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
{"readtime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
{"SoccerMLS", 0, 0, GID_SOCCER, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
{"spyozon", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index a48e54c05a..729e66b310 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -36,8 +36,8 @@
#include "gui/about.h"
-#include "gui/GuiManager.h"
-#include "gui/ListWidget.h"
+#include "gui/gui-manager.h"
+#include "gui/widgets/list.h"
#include "gui/ThemeEval.h"
#include "scumm/dialogs.h"
@@ -45,7 +45,6 @@
#include "scumm/scumm.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/player_v2.h"
#include "scumm/verbs.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
@@ -58,8 +57,6 @@
#include "gui/KeysDialog.h"
#endif
-using GUI::CommandSender;
-using GUI::StaticTextWidget;
using Graphics::kTextAlignCenter;
using Graphics::kTextAlignLeft;
using GUI::WIDGET_ENABLED;
@@ -258,7 +255,7 @@ ScummMenuDialog::~ScummMenuDialog() {
delete _helpDialog;
}
-void ScummMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+void ScummMenuDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
switch (cmd) {
case kHelpCmd:
_helpDialog->runModal();
@@ -277,7 +274,7 @@ enum {
HelpDialog::HelpDialog(const GameSettings &game)
: ScummDialog("ScummHelp"), _game(game) {
- _title = new StaticTextWidget(this, "ScummHelp.Title", "");
+ _title = new GUI::StaticTextWidget(this, "ScummHelp.Title", "");
_page = 1;
_backgroundType = GUI::ThemeEngine::kDialogBackgroundDefault;
@@ -293,8 +290,8 @@ HelpDialog::HelpDialog(const GameSettings &game)
// Dummy entries
for (int i = 0; i < HELP_NUM_LINES; i++) {
- _key[i] = new StaticTextWidget(this, 0, 0, 10, 10, "", Graphics::kTextAlignRight);
- _dsc[i] = new StaticTextWidget(this, 0, 0, 10, 10, "", Graphics::kTextAlignLeft);
+ _key[i] = new GUI::StaticTextWidget(this, 0, 0, 10, 10, "", Graphics::kTextAlignRight);
+ _dsc[i] = new GUI::StaticTextWidget(this, 0, 0, 10, 10, "", Graphics::kTextAlignLeft);
}
}
@@ -346,7 +343,7 @@ void HelpDialog::displayKeyBindings() {
delete[] dscStr;
}
-void HelpDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+void HelpDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
switch (cmd) {
case kNextCmd:
@@ -386,7 +383,7 @@ InfoDialog::InfoDialog(ScummEngine *scumm, int res)
_message = queryResString(res);
// Width and height are dummy
- _text = new StaticTextWidget(this, 0, 0, 10, 10, _message, kTextAlignCenter);
+ _text = new GUI::StaticTextWidget(this, 0, 0, 10, 10, _message, kTextAlignCenter);
}
InfoDialog::InfoDialog(ScummEngine *scumm, const String& message)
@@ -395,7 +392,7 @@ InfoDialog::InfoDialog(ScummEngine *scumm, const String& message)
_message = message;
// Width and height are dummy
- _text = new StaticTextWidget(this, 0, 0, 10, 10, _message, kTextAlignCenter);
+ _text = new GUI::StaticTextWidget(this, 0, 0, 10, 10, _message, kTextAlignCenter);
}
void InfoDialog::setInfoText(const String& message) {
diff --git a/engines/scumm/file.cpp b/engines/scumm/file.cpp
index 0081720d03..20050e5d4c 100644
--- a/engines/scumm/file.cpp
+++ b/engines/scumm/file.cpp
@@ -27,7 +27,8 @@
#include "scumm/scumm.h"
-using Common::File;
+#include "common/memstream.h"
+#include "common/substream.h"
namespace Scumm {
@@ -35,11 +36,7 @@ namespace Scumm {
#pragma mark --- ScummFile ---
#pragma mark -
-ScummFile::ScummFile() : _encbyte(0), _subFileStart(0), _subFileLen(0) {
-}
-
-void ScummFile::setEnc(byte value) {
- _encbyte = value;
+ScummFile::ScummFile() : _subFileStart(0), _subFileLen(0) {
}
void ScummFile::setSubfileRange(int32 start, int32 len) {
@@ -248,10 +245,6 @@ ScummDiskImage::ScummDiskImage(const char *disk1, const char *disk2, GameSetting
}
}
-void ScummDiskImage::setEnc(byte enc) {
- _stream->setEnc(enc);
-}
-
byte ScummDiskImage::fileReadByte() {
byte b = 0;
File::read(&b, 1);
@@ -499,4 +492,17 @@ bool ScummDiskImage::openSubFile(const Common::String &filename) {
return true;
}
+uint32 ScummDiskImage::read(void *dataPtr, uint32 dataSize) {
+ uint32 realLen = _stream->read(dataPtr, dataSize);
+
+ if (_encbyte) {
+ byte *p = (byte *)dataPtr;
+ byte *end = p + realLen;
+ while (p < end)
+ *p++ ^= _encbyte;
+ }
+
+ return realLen;
+}
+
} // End of namespace Scumm
diff --git a/engines/scumm/file.h b/engines/scumm/file.h
index c37c2f036e..8a25277ded 100644
--- a/engines/scumm/file.h
+++ b/engines/scumm/file.h
@@ -27,14 +27,19 @@
#define SCUMM_FILE_H
#include "common/file.h"
+#include "common/stream.h"
#include "scumm/detection.h"
namespace Scumm {
class BaseScummFile : public Common::File {
+protected:
+ byte _encbyte;
+
public:
- virtual void setEnc(byte value) = 0;
+ BaseScummFile() : _encbyte(0) {}
+ void setEnc(byte value) { _encbyte = value; }
virtual bool open(const Common::String &filename) = 0;
virtual bool openSubFile(const Common::String &filename) = 0;
@@ -52,7 +57,6 @@ public:
class ScummFile : public BaseScummFile {
private:
- byte _encbyte;
int32 _subFileStart;
int32 _subFileLen;
bool _myEos; // Have we read past the end of the subfile?
@@ -62,7 +66,6 @@ private:
public:
ScummFile();
- void setEnc(byte value);
bool open(const Common::String &filename);
bool openSubFile(const Common::String &filename);
@@ -78,7 +81,7 @@ public:
class ScummDiskImage : public BaseScummFile {
private:
- Common::MemoryReadStream *_stream;
+ Common::SeekableReadStream *_stream;
byte _roomDisks[59], _roomTracks[59], _roomSectors[59];
byte *_buf;
@@ -108,7 +111,6 @@ private:
public:
ScummDiskImage(const char *disk1, const char *disk2, GameSettings game);
- void setEnc(byte value);
bool open(const Common::String &filename);
bool openSubFile(const Common::String &filename);
@@ -118,7 +120,7 @@ public:
int32 pos() const { return _stream->pos(); }
int32 size() const { return _stream->size(); }
bool seek(int32 offs, int whence = SEEK_SET) { return _stream->seek(offs, whence); }
- uint32 read(void *dataPtr, uint32 dataSize) { return _stream->read(dataPtr, dataSize); }
+ uint32 read(void *dataPtr, uint32 dataSize);
};
} // End of namespace Scumm
diff --git a/engines/scumm/file_nes.cpp b/engines/scumm/file_nes.cpp
index 0caf63b410..d9d84f04ff 100644
--- a/engines/scumm/file_nes.cpp
+++ b/engines/scumm/file_nes.cpp
@@ -27,8 +27,7 @@
#include "common/debug.h"
#include "common/endian.h"
#include "common/md5.h"
-
-using Common::File;
+#include "common/memstream.h"
namespace Scumm {
@@ -49,10 +48,6 @@ struct ScummNESFile::ResourceGroup {
ScummNESFile::ScummNESFile() : _stream(0), _buf(0), _ROMset(kROMsetNum) {
}
-void ScummNESFile::setEnc(byte enc) {
- _stream->setEnc(enc);
-}
-
static const ScummNESFile::Resource res_roomgfx_usa[40] = {
{ 0x04001, 0x03C9 }, { 0x043CA, 0x069E }, { 0x04A68, 0x0327 }, { 0x04D8F, 0x053B }, { 0x052CA, 0x06BE },
{ 0x05988, 0x0682 }, { 0x0600A, 0x0778 }, { 0x06782, 0x0517 }, { 0x06C99, 0x07FB }, { 0x07494, 0x07BE },
@@ -1077,7 +1072,7 @@ uint16 ScummNESFile::extractResource(Common::WriteStream *output, const Resource
case NES_PREPLIST:
len = res->length;
- reslen += write_word(output, 0x002A);
+ reslen += write_word(output, 0x002A);
reslen += write_byte(output, ' ');
for (i = 1; i < 8; i++)
@@ -1371,34 +1366,36 @@ bool ScummNESFile::generateIndex() {
bool ScummNESFile::open(const Common::String &filename) {
if (_ROMset == kROMsetNum) {
- char md5str[32+1];
+ Common::String md5str;
File f;
f.open(filename);
- if (f.isOpen() && Common::md5_file_string(f, md5str)) {
+ if (f.isOpen())
+ md5str = Common::computeStreamMD5AsString(f);
+ if (!md5str.empty()) {
- if (!strcmp(md5str, "3905799e081b80a61d4460b7b733c206")) {
+ if (md5str == "3905799e081b80a61d4460b7b733c206") {
_ROMset = kROMsetUSA;
debug(1, "ROM contents verified as Maniac Mansion (USA)");
- } else if (!strcmp(md5str, "d8d07efcb88f396bee0b402b10c3b1c9")) {
+ } else if (md5str == "d8d07efcb88f396bee0b402b10c3b1c9") {
_ROMset = kROMsetEurope;
debug(1, "ROM contents verified as Maniac Mansion (Europe)");
- } else if (!strcmp(md5str, "22d07d6c386c9c25aca5dac2a0c0d94b")) {
+ } else if (md5str == "22d07d6c386c9c25aca5dac2a0c0d94b") {
_ROMset = kROMsetSweden;
debug(1, "ROM contents verified as Maniac Mansion (Sweden)");
- } else if (!strcmp(md5str, "81bbfa181184cb494e7a81dcfa94fbd9")) {
+ } else if (md5str == "81bbfa181184cb494e7a81dcfa94fbd9") {
_ROMset = kROMsetFrance;
debug(2, "ROM contents verified as Maniac Mansion (France)");
- } else if (!strcmp(md5str, "257f8c14d8c584f7ddd601bcb00920c7")) {
+ } else if (md5str == "257f8c14d8c584f7ddd601bcb00920c7") {
_ROMset = kROMsetGermany;
debug(2, "ROM contents verified as Maniac Mansion (Germany)");
- } else if (!strcmp(md5str, "f163cf53f7850e43fb482471e5c52e1a")) {
+ } else if (md5str == "f163cf53f7850e43fb482471e5c52e1a") {
_ROMset = kROMsetSpain;
debug(2, "ROM contents verified as Maniac Mansion (Spain)");
- } else if (!strcmp(md5str, "54a68a5f5e3c86da42b7ca5f51e79b1d")) {
+ } else if (md5str == "54a68a5f5e3c86da42b7ca5f51e79b1d") {
_ROMset = kROMsetItaly;
debug(2, "ROM contents verified as Maniac Mansion (Italy)");
} else {
- error("Unsupported Maniac Mansion ROM, md5: %s", md5str);
+ error("Unsupported Maniac Mansion ROM, md5: %s", md5str.c_str());
return false;
}
} else {
@@ -1450,5 +1447,17 @@ bool ScummNESFile::openSubFile(const Common::String &filename) {
}
}
+uint32 ScummNESFile::read(void *dataPtr, uint32 dataSize) {
+ uint32 realLen = _stream->read(dataPtr, dataSize);
+
+ if (_encbyte) {
+ byte *p = (byte *)dataPtr;
+ byte *end = p + realLen;
+ while (p < end)
+ *p++ ^= _encbyte;
+ }
+
+ return realLen;
+}
} // End of namespace Scumm
diff --git a/engines/scumm/file_nes.h b/engines/scumm/file_nes.h
index 274ec02ed0..f2ce3e19fd 100644
--- a/engines/scumm/file_nes.h
+++ b/engines/scumm/file_nes.h
@@ -26,8 +26,6 @@
#ifndef SCUMM_FILE_NES_H
#define SCUMM_FILE_NES_H
-#include "common/file.h"
-
#include "scumm/file.h"
namespace Scumm {
@@ -70,7 +68,7 @@ public:
private:
- Common::MemoryReadStream *_stream;
+ Common::SeekableReadStream *_stream;
ROMset _ROMset;
byte *_buf;
@@ -83,7 +81,6 @@ private:
public:
ScummNESFile();
- void setEnc(byte value);
bool open(const Common::String &filename);
bool openSubFile(const Common::String &filename);
@@ -93,7 +90,7 @@ public:
int32 pos() const { return _stream->pos(); }
int32 size() const { return _stream->size(); }
bool seek(int32 offs, int whence = SEEK_SET) { return _stream->seek(offs, whence); }
- uint32 read(void *dataPtr, uint32 dataSize) { return _stream->read(dataPtr, dataSize); }
+ uint32 read(void *dataPtr, uint32 dataSize);
};
} // End of namespace Scumm
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index e7c81bd418..313e8b88c0 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1013,11 +1013,6 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
if (rect.left > vs->w)
return;
-
-#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154)
- rect.right = 320;
-#endif
// Convert 'rect' to local (virtual screen) coordinates
rect.top -= vs->topline;
@@ -1025,13 +1020,18 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
rect.clip(vs->w, vs->h);
+ const int height = rect.height();
+ const int width = rect.width();
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154)
+ rect.right = 319;
+#endif
+
markRectAsDirty(vs->number, rect, USAGE_BIT_RESTORED);
screenBuf = vs->getPixels(rect.left, rect.top);
- const int height = rect.height();
- const int width = rect.width();
-
if (!height)
return;
@@ -2457,7 +2457,7 @@ void ScummEngine::decodeNESBaseTiles() {
}
static const int v1MMNEScostTables[2][6] = {
- /* desc lens offs data gfx pal */
+ /* desc lens offs data gfx pal */
{ 25, 27, 29, 31, 33, 35},
{ 26, 28, 30, 32, 34, 36}
};
@@ -3737,6 +3737,10 @@ void ScummEngine::fadeOut(int effect) {
// Just blit screen 0 to the display (i.e. display will be black)
vs->setDirtyRange(0, vs->h);
updateDirtyScreen(kMainVirtScreen);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen)
+ _townsScreen->update();
+#endif
break;
case 134:
dissolveEffect(1, 1);
diff --git a/engines/scumm/he/animation_he.h b/engines/scumm/he/animation_he.h
index 34794b35ac..af4ec2041d 100644
--- a/engines/scumm/he/animation_he.h
+++ b/engines/scumm/he/animation_he.h
@@ -26,8 +26,6 @@
#if !defined(SCUMM_HE_ANIMATION_H) && defined(ENABLE_HE)
#define SCUMM_HE_ANIMATION_H
-#include "common/file.h"
-
#include "graphics/video/smk_decoder.h"
#include "sound/mixer.h"
diff --git a/engines/scumm/he/cup_player_he.cpp b/engines/scumm/he/cup_player_he.cpp
index 87afe5fd90..fedbdbf377 100644
--- a/engines/scumm/he/cup_player_he.cpp
+++ b/engines/scumm/he/cup_player_he.cpp
@@ -26,6 +26,7 @@
#ifdef ENABLE_HE
#include "common/system.h"
+#include "common/memstream.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
diff --git a/engines/scumm/he/logic_he.cpp b/engines/scumm/he/logic_he.cpp
index add9b982e2..ed92c33105 100644
--- a/engines/scumm/he/logic_he.cpp
+++ b/engines/scumm/he/logic_he.cpp
@@ -77,11 +77,11 @@ int32 LogicHE::dispatch(int op, int numArgs, int32 *args) {
#if 1
Common::String str;
- str = Common::String::printf("LogicHE::dispatch(%d, %d, [", op, numArgs);
+ str = Common::String::format("LogicHE::dispatch(%d, %d, [", op, numArgs);
if (numArgs > 0)
- str += Common::String::printf("%d", args[0]);
+ str += Common::String::format("%d", args[0]);
for (int i = 1; i < numArgs; i++) {
- str += Common::String::printf(", %d", args[i]);
+ str += Common::String::format(", %d", args[i]);
}
str += "])";
@@ -232,7 +232,7 @@ int32 LogicHErace::op_1101(int32 *args) {
int32 retval;
float temp;
- temp = args[0] / _userData[532];
+ temp = args[0] / _userData[532];
if (_userData[519] != temp) {
_userData[519] = temp;
op_sub3(temp);
@@ -955,6 +955,30 @@ int LogicHEsoccer::op_1021(int32 *args) {
}
/***********************
+ * Backyard Baseball 2001
+ *
+ */
+
+int LogicHEbaseball2001::versionID() {
+ return 1;
+}
+
+int32 LogicHEbaseball2001::dispatch(int op, int numArgs, int32 *args) {
+ int res = 0;
+
+ switch (op) {
+ case 3001:
+ // Check network status
+ break;
+
+ default:
+ LogicHE::dispatch(op, numArgs, args);
+ }
+
+ return res;
+}
+
+/***********************
* Backyard Basketball
*
*/
diff --git a/engines/scumm/he/logic_he.h b/engines/scumm/he/logic_he.h
index 7dd141c5b1..ab952abd5e 100644
--- a/engines/scumm/he/logic_he.h
+++ b/engines/scumm/he/logic_he.h
@@ -133,6 +133,14 @@ private:
int op_1021(int32 *args);
};
+class LogicHEbaseball2001 : public LogicHE {
+public:
+ LogicHEbaseball2001(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+};
+
class LogicHEbasketball : public LogicHE {
public:
LogicHEbasketball(ScummEngine_v90he *vm) : LogicHE(vm) {}
diff --git a/engines/scumm/he/palette_he.cpp b/engines/scumm/he/palette_he.cpp
index 6ef68d981e..ad3f90b8eb 100644
--- a/engines/scumm/he/palette_he.cpp
+++ b/engines/scumm/he/palette_he.cpp
@@ -203,8 +203,8 @@ void ScummEngine_v90he::setHEPaletteFromImage(int palSlot, int resId, int state)
uint8 *data = getResourceAddress(rtImage, resId);
assert(data);
const uint8 *rgbs = findWrappedBlock(MKID_BE('RGBS'), data, state, 0);
- assert(rgbs);
- setHEPaletteFromPtr(palSlot, rgbs);
+ if (rgbs)
+ setHEPaletteFromPtr(palSlot, rgbs);
}
void ScummEngine_v90he::setHEPaletteFromRoom(int palSlot, int resId, int state) {
diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp
index c259c3ffd2..09b7b54253 100644
--- a/engines/scumm/he/resource_he.cpp
+++ b/engines/scumm/he/resource_he.cpp
@@ -23,7 +23,6 @@
*
*/
-
#include "scumm/scumm.h"
#include "scumm/file.h"
#include "scumm/he/intern_he.h"
@@ -35,14 +34,18 @@
#include "graphics/cursorman.h"
#include "common/archive.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/system.h"
namespace Scumm {
+#if defined(SCUMM_LITTLE_ENDIAN)
+#define LE16(x)
+#define LE32(x)
+#elif defined(SCUMM_BIG_ENDIAN)
#define LE16(x) ((x) = TO_LE_16(x))
#define LE32(x) ((x) = TO_LE_32(x))
-
+#endif
ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
: _vm(scumm) {
@@ -207,9 +210,9 @@ int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte
}
-/* res_type_id_to_string:
- * Translate a numeric resource type to it's corresponding string type.
- * (For informative-ness.)
+/**
+ * Translate a numeric resource type to it's corresponding string type.
+ * (For informative-ness.)
*/
const char *Win32ResExtractor::res_type_id_to_string(int id) {
if (id == 241)
@@ -219,9 +222,9 @@ const char *Win32ResExtractor::res_type_id_to_string(int id) {
return NULL;
}
-/* res_type_string_to_id:
- * Translate a resource type string to integer.
- * (Used to convert the --type option.)
+/**
+ * Translate a resource type string to integer.
+ * (Used to convert the --type option.)
*/
const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
static const char *res_type_ids[] = {
@@ -242,22 +245,18 @@ const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
return type;
}
-/* return the resource id quoted if it's a string, otherwise just return it */
-char *Win32ResExtractor::WinResource::get_resource_id_quoted() {
- // FIXME: Using a static var here is EVIL and in fact, broken when
- // used multiple times in a row, e.g. in a single call to printf()
- // or debug()... which is in fact how we use this function... :-)
- static char tmp[WINRES_ID_MAXLEN+2];
-
+/**
+ * Return the resource id quoted if it is a string, otherwise (i.e. if
+ * it is numeric) just return it.
+ */
+Common::String Win32ResExtractor::WinResource::getQuotedResourceId() const {
if (numeric_id || id[0] == '\0')
return id;
-
- sprintf(tmp, "'%s'", id);
- return tmp;
+ return '"' + Common::String(id) + '"';
}
int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
- WinResource *type_wr, WinResource *name_wr,
+ WinResource *type_wr, WinResource *name_wr,
WinResource *lang_wr, byte **data) {
int size;
bool free_it;
@@ -281,19 +280,21 @@ int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
if ((id = strtol(type_wr->id, 0, 10)) != 0)
type = res_type_id_to_string(id);
- debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s%s%s [size=%d]",
- name_wr->get_resource_id_quoted(),
- (lang_wr->id[0] != '\0' ? " language: " : ""),
- lang_wr->get_resource_id_quoted(), size);
-
+ if (lang_wr != NULL && lang_wr->id[0] != '\0') {
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s language: %s [size=%d]",
+ name_wr->getQuotedResourceId().c_str(), lang_wr->getQuotedResourceId().c_str(), size);
+ } else {
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s [size=%d]",
+ name_wr->getQuotedResourceId().c_str(), size);
+ }
return size;
}
-/* extract_resource:
- * Extract a resource, returning pointer to data.
+/**
+ * Extract a resource, returning pointer to data.
*/
byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size,
- bool *free_it, char *type, char *lang, bool raw) {
+ bool *free_it, char *type, char *lang, bool raw) {
char *str;
int32 intval;
@@ -320,20 +321,20 @@ byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *
return NULL;
}
-/* extract_group_icon_resource:
- * Create a complete RT_GROUP_ICON resource, that can be written to
- * an `.ico' file without modifications. Returns an allocated
- * memory block that should be freed with free() once used.
+/**
+ * Create a complete RT_GROUP_ICON resource, that can be written to
+ * an `.ico' file without modifications. Returns an allocated
+ * memory block that should be freed with free() once used.
*
- * `root' is the offset in file that specifies the resource.
- * `base' is the offset that string pointers are calculated from.
- * `ressize' should point to an integer variable where the size of
- * the returned memory block will be placed.
- * `is_icon' indicates whether resource to be extracted is icon
- * or cursor group.
+ * `root' is the offset in file that specifies the resource.
+ * `base' is the offset that string pointers are calculated from.
+ * `ressize' should point to an integer variable where the size of
+ * the returned memory block will be placed.
+ * `is_icon' indicates whether resource to be extracted is icon
+ * or cursor group.
*/
byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang,
- int *ressize, bool is_icon) {
+ int *ressize, bool is_icon) {
Win32CursorIconDir *icondir;
Win32CursorIconFileDir *fileicondir;
byte *memory;
@@ -357,7 +358,7 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
WinResource *fwr;
RETURN_IF_BAD_POINTER(NULL, icondir->entries[c]);
- /*printf("%d. bytes_in_res=%d width=%d height=%d planes=%d bit_count=%d\n", c,
+ /*debug("%d. bytes_in_res=%d width=%d height=%d planes=%d bit_count=%d", c,
FROM_LE_32(icondir->entries[c].bytes_in_res),
(is_icon ? icondir->entries[c].res_info.icon.width : FROM_LE_16(icondir->entries[c].res_info.cursor.width)),
(is_icon ? icondir->entries[c].res_info.icon.height : FROM_LE_16(icondir->entries[c].res_info.cursor.height)),
@@ -374,20 +375,20 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
}
if (get_resource_entry(fi, fwr, &iconsize) != NULL) {
- if (iconsize == 0) {
+ if (iconsize == 0) {
debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", _fileName.c_str(), name);
skipped++;
continue;
- }
- if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) {
+ }
+ if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) {
debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)",
_fileName.c_str(), name, iconsize, FROM_LE_32(icondir->entries[c].bytes_in_res));
- }
- size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */
+ }
+ size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */
- /* cursor resources have two additional WORDs that contain
- * hotspot info */
- if (!is_icon)
+ /* cursor resources have two additional WORDs that contain
+ * hotspot info */
+ if (!is_icon)
size -= sizeof(uint16)*2;
}
}
@@ -428,8 +429,8 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
return NULL;
}
if (size == 0) {
- skipped++;
- continue;
+ skipped++;
+ continue;
}
/* copy ICONDIRENTRY (not including last dwImageOffset) */
@@ -465,10 +466,10 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
return memory;
}
-/* check_offset:
- * Check if a chunk of data (determined by offset and size)
- * is within the bounds of the WinLibrary file.
- * Usually not called directly.
+/**
+ * Check if a chunk of data (determined by offset and size)
+ * is within the bounds of the WinLibrary file.
+ * Usually not called directly.
*/
bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) {
int need_size = (int)((byte *)offset - memory + size);
@@ -485,8 +486,8 @@ bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *n
}
-/* do_resources:
- * Do something for each resource matching type, name and lang.
+/**
+ * Do something for each resource matching type, name and lang.
*/
int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, byte **data) {
WinResource *type_wr;
@@ -494,7 +495,7 @@ int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name
WinResource *lang_wr;
int size;
- type_wr = (WinResource *)calloc(sizeof(WinResource)*3, 1);
+ type_wr = (WinResource *)calloc(3, sizeof(WinResource));
name_wr = type_wr + 1;
lang_wr = type_wr + 2;
@@ -521,14 +522,11 @@ int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base,
/* get a list of all resources at this level */
wr = list_resources(fi, base, &rescnt);
if (wr == NULL) {
- if (size != 0)
- return size;
- else
- return 0;
+ return size;
}
/* process each resource listed */
- for (c = 0 ; c < rescnt ; c++) {
+ for (c = 0; c < rescnt; c++) {
/* (over)write the corresponding WinResource holder with the current */
memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource));
@@ -555,7 +553,9 @@ bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) {
return false;
if (id[0] == '-')
id++;
- if (!(cmp1 = strtol(wr->id, 0, 10)) || !(cmp2 = strtol(id, 0, 10)) || cmp1 != cmp2)
+ cmp1 = strtol(wr->id, 0, 10);
+ cmp2 = strtol(id, 0, 10);
+ if (!cmp1 || !cmp2 || cmp1 != cmp2)
return false;
} else {
if (id[0] == '-')
@@ -643,9 +643,9 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary
}
-/* list_resources:
- * Return an array of WinResource's in the current
- * resource level specified by _res->
+/**
+ * Return an array of WinResource's in the current
+ * resource level specified by _res->
*/
Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) {
if (res != NULL && !res->is_directory)
@@ -657,10 +657,9 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi
count);
}
-/* read_library:
- * Read header and get resource directory offset in a Windows library
- * (AKA module).
- *
+/**
+ * Read header and get resource directory offset in a Windows library
+ * (AKA module).
*/
bool Win32ResExtractor::read_library(WinLibrary *fi) {
/* check for DOS header signature `MZ' */
@@ -695,7 +694,9 @@ bool Win32ResExtractor::read_library(WinLibrary *fi) {
/* calc_vma_size has reported error */
return false;
}
- fi->memory = (byte *)realloc(fi->memory, fi->total_size);
+ byte *ptr = (byte *)realloc(fi->memory, fi->total_size);
+ assert(ptr);
+ fi->memory = ptr;
/* relocate memory, start from last section */
pe_header = PE_HEADER(fi->memory);
@@ -739,13 +740,13 @@ bool Win32ResExtractor::read_library(WinLibrary *fi) {
return false;
}
-/* calc_vma_size:
- * Calculate the total amount of memory needed for a 32-bit Windows
- * module. Returns -1 if file was too small.
+/**
+ * Calculate the total amount of memory needed for a 32-bit Windows
+ * module. Returns -1 if file was too small.
*/
int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
- Win32ImageSectionHeader *seg;
- int c, segcount, size;
+ Win32ImageSectionHeader *seg;
+ int c, segcount, size;
size = 0;
RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections);
@@ -760,7 +761,7 @@ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
seg = PE_SECTIONS(fi->memory);
RETURN_IF_BAD_POINTER(-1, *seg);
- for (c = 0 ; c < segcount ; c++) {
+ for (c = 0 ; c < segcount ; c++) {
RETURN_IF_BAD_POINTER(0, *seg);
fix_win32_image_section_header(seg);
@@ -768,9 +769,9 @@ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
/* I have no idea what misc.virtual_size is for... */
size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size);
seg++;
- }
+ }
- return size;
+ return size;
}
Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) {
@@ -831,7 +832,7 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int
uint32 c, d;
int completed;
int matched = 0;
- MemoryReadStream *in = new MemoryReadStream(data, datasize);
+ Common::MemoryReadStream *in = new Common::MemoryReadStream(data, datasize);
if (!in->read(&dir, sizeof(Win32CursorIconFileDir)- sizeof(Win32CursorIconFileDirEntry)))
goto cleanup;
@@ -914,8 +915,8 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int
if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)",
- entries[c].dib_size,
- (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
+ entries[c].dib_size,
+ (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
);
image_data = (byte *)malloc(image_size);
@@ -977,9 +978,9 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int
row[4*x+2] = (color >> 0) & 0xFF;
}
if (bitmap.bit_count == 32)
- row[4*x+3] = (color >> 24) & 0xFF;
+ row[4*x+3] = (color >> 24) & 0xFF;
else
- row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
+ row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
*/
}
@@ -1051,93 +1052,93 @@ uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) {
}
void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) {
- LE16(obj->reserved);
+ LE16(obj->reserved);
LE16(obj->type);
- LE16(obj->count);
+ LE16(obj->count);
}
void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) {
- LE32(obj->size);
- LE32(obj->width);
- LE32(obj->height);
- LE16(obj->planes);
- LE16(obj->bit_count);
- LE32(obj->compression);
- LE32(obj->size_image);
- LE32(obj->x_pels_per_meter);
- LE32(obj->y_pels_per_meter);
- LE32(obj->clr_used);
- LE32(obj->clr_important);
+ LE32(obj->size);
+ LE32(obj->width);
+ LE32(obj->height);
+ LE16(obj->planes);
+ LE16(obj->bit_count);
+ LE32(obj->compression);
+ LE32(obj->size_image);
+ LE32(obj->x_pels_per_meter);
+ LE32(obj->y_pels_per_meter);
+ LE32(obj->clr_used);
+ LE32(obj->clr_important);
}
void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) {
- LE16(obj->hotspot_x);
- LE16(obj->hotspot_y);
- LE32(obj->dib_size);
- LE32(obj->dib_offset);
+ LE16(obj->hotspot_x);
+ LE16(obj->hotspot_y);
+ LE32(obj->dib_size);
+ LE32(obj->dib_offset);
}
void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) {
- LE32(obj->misc.physical_address);
- LE32(obj->virtual_address);
- LE32(obj->size_of_raw_data);
- LE32(obj->pointer_to_raw_data);
- LE32(obj->pointer_to_relocations);
- LE32(obj->pointer_to_linenumbers);
- LE16(obj->number_of_relocations);
- LE16(obj->number_of_linenumbers);
- LE32(obj->characteristics);
+ LE32(obj->misc.physical_address);
+ LE32(obj->virtual_address);
+ LE32(obj->size_of_raw_data);
+ LE32(obj->pointer_to_raw_data);
+ LE32(obj->pointer_to_relocations);
+ LE32(obj->pointer_to_linenumbers);
+ LE16(obj->number_of_relocations);
+ LE16(obj->number_of_linenumbers);
+ LE32(obj->characteristics);
}
/* fix_win32_image_header_endian:
* NOTE: This assumes that the optional header is always available.
*/
void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) {
- LE32(obj->signature);
- LE16(obj->file_header.machine);
- LE16(obj->file_header.number_of_sections);
- LE32(obj->file_header.time_date_stamp);
- LE32(obj->file_header.pointer_to_symbol_table);
- LE32(obj->file_header.number_of_symbols);
- LE16(obj->file_header.size_of_optional_header);
- LE16(obj->file_header.characteristics);
+ LE32(obj->signature);
+ LE16(obj->file_header.machine);
+ LE16(obj->file_header.number_of_sections);
+ LE32(obj->file_header.time_date_stamp);
+ LE32(obj->file_header.pointer_to_symbol_table);
+ LE32(obj->file_header.number_of_symbols);
+ LE16(obj->file_header.size_of_optional_header);
+ LE16(obj->file_header.characteristics);
// FIXME: Does this assert ever trigger? If so, we should modify this function
// to properly deal with it.
assert(obj->file_header.size_of_optional_header >= sizeof(obj->optional_header));
- LE16(obj->optional_header.magic);
- LE32(obj->optional_header.size_of_code);
- LE32(obj->optional_header.size_of_initialized_data);
- LE32(obj->optional_header.size_of_uninitialized_data);
- LE32(obj->optional_header.address_of_entry_point);
- LE32(obj->optional_header.base_of_code);
- LE32(obj->optional_header.base_of_data);
- LE32(obj->optional_header.image_base);
- LE32(obj->optional_header.section_alignment);
- LE32(obj->optional_header.file_alignment);
- LE16(obj->optional_header.major_operating_system_version);
- LE16(obj->optional_header.minor_operating_system_version);
- LE16(obj->optional_header.major_image_version);
- LE16(obj->optional_header.minor_image_version);
- LE16(obj->optional_header.major_subsystem_version);
- LE16(obj->optional_header.minor_subsystem_version);
- LE32(obj->optional_header.win32_version_value);
- LE32(obj->optional_header.size_of_image);
- LE32(obj->optional_header.size_of_headers);
- LE32(obj->optional_header.checksum);
- LE16(obj->optional_header.subsystem);
- LE16(obj->optional_header.dll_characteristics);
- LE32(obj->optional_header.size_of_stack_reserve);
- LE32(obj->optional_header.size_of_stack_commit);
- LE32(obj->optional_header.size_of_heap_reserve);
- LE32(obj->optional_header.size_of_heap_commit);
- LE32(obj->optional_header.loader_flags);
- LE32(obj->optional_header.number_of_rva_and_sizes);
+ LE16(obj->optional_header.magic);
+ LE32(obj->optional_header.size_of_code);
+ LE32(obj->optional_header.size_of_initialized_data);
+ LE32(obj->optional_header.size_of_uninitialized_data);
+ LE32(obj->optional_header.address_of_entry_point);
+ LE32(obj->optional_header.base_of_code);
+ LE32(obj->optional_header.base_of_data);
+ LE32(obj->optional_header.image_base);
+ LE32(obj->optional_header.section_alignment);
+ LE32(obj->optional_header.file_alignment);
+ LE16(obj->optional_header.major_operating_system_version);
+ LE16(obj->optional_header.minor_operating_system_version);
+ LE16(obj->optional_header.major_image_version);
+ LE16(obj->optional_header.minor_image_version);
+ LE16(obj->optional_header.major_subsystem_version);
+ LE16(obj->optional_header.minor_subsystem_version);
+ LE32(obj->optional_header.win32_version_value);
+ LE32(obj->optional_header.size_of_image);
+ LE32(obj->optional_header.size_of_headers);
+ LE32(obj->optional_header.checksum);
+ LE16(obj->optional_header.subsystem);
+ LE16(obj->optional_header.dll_characteristics);
+ LE32(obj->optional_header.size_of_stack_reserve);
+ LE32(obj->optional_header.size_of_stack_commit);
+ LE32(obj->optional_header.size_of_heap_reserve);
+ LE32(obj->optional_header.size_of_heap_commit);
+ LE32(obj->optional_header.loader_flags);
+ LE32(obj->optional_header.number_of_rva_and_sizes);
}
void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) {
- LE32(obj->virtual_address);
- LE32(obj->size);
+ LE32(obj->virtual_address);
+ LE32(obj->size);
}
diff --git a/engines/scumm/he/resource_he.h b/engines/scumm/he/resource_he.h
index 4567c598a3..b3f3479406 100644
--- a/engines/scumm/he/resource_he.h
+++ b/engines/scumm/he/resource_he.h
@@ -147,9 +147,6 @@ public:
bool _arg_raw;
Common::String _fileName;
CachedCursor _cursorCache[MAX_CACHED_CURSORS];
-
- typedef Common::MemoryReadStream MemoryReadStream;
-
};
class Win32ResExtractor : public ResExtractor {
@@ -184,7 +181,7 @@ class Win32ResExtractor : public ResExtractor {
bool numeric_id;
bool is_directory;
- char *get_resource_id_quoted();
+ Common::String getQuotedResourceId() const;
} PACKED_STRUCT;
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 3555f55d95..ca4a65ac74 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -34,7 +34,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/he/sprite_he.h"
@@ -1623,13 +1622,11 @@ void ScummEngine_v100he::o100_roomOps() {
case 137:
byte buffer[256];
- int r;
copyScriptString((byte *)buffer, sizeof(buffer));
- r = convertFilePath(buffer, sizeof(buffer));
- memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r);
- debug(1, "o100_roomOps: case 137: filename %s", _saveLoadFileName);
+ _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer));
+ debug(1, "o100_roomOps: case 137: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
diff --git a/engines/scumm/he/script_v60he.cpp b/engines/scumm/he/script_v60he.cpp
index 8ade78c1b5..9d62a31f6d 100644
--- a/engines/scumm/he/script_v60he.cpp
+++ b/engines/scumm/he/script_v60he.cpp
@@ -283,15 +283,14 @@ void ScummEngine_v60he::o60_roomOps() {
break;
case 221:
byte buffer[100];
- int len, r;
+ int len;
convertMessageToString(_scriptPointer, buffer, sizeof(buffer));
len = resStrLen(_scriptPointer);
_scriptPointer += len + 1;
- r = convertFilePath(buffer, sizeof(buffer));
- memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r);
- debug(1, "o60_roomOps: case 221: filename %s", _saveLoadFileName);
+ _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer));
+ debug(1, "o60_roomOps: case 221: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index 979b2b3df4..9b160151b0 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -31,7 +31,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/verbs.h"
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index baa57c7821..32c15abcba 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -36,7 +36,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/util.h"
@@ -173,7 +172,7 @@ int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
}
const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
- (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
+ (idx2 - FROM_LE_32(ah->dim2start)) + (idx1 - FROM_LE_32(ah->dim1start));
switch (FROM_LE_32(ah->type)) {
case kByteArray:
@@ -711,13 +710,11 @@ void ScummEngine_v72he::o72_roomOps() {
case 221:
byte buffer[256];
- int r;
copyScriptString((byte *)buffer, sizeof(buffer));
- r = convertFilePath(buffer, sizeof(buffer));
- memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r);
- debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName);
+ _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer));
+ debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
@@ -1415,12 +1412,7 @@ void ScummEngine_v72he::o72_openFile() {
if (!_saveFileMan->listSavefiles(filename).empty()) {
_hInFileTable[slot] = _saveFileMan->openForLoading(filename);
} else {
- Common::File *f = new Common::File();
- f->open(filename);
- if (!f->isOpen())
- delete f;
- else
- _hInFileTable[slot] = f;
+ _hInFileTable[slot] = SearchMan.createReadStreamForMember(filename);
}
break;
case 2:
diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
index dd44180fa0..c225be4b6c 100644
--- a/engines/scumm/he/script_v80he.cpp
+++ b/engines/scumm/he/script_v80he.cpp
@@ -25,6 +25,7 @@
#ifdef ENABLE_HE
+#include "common/archive.h"
#include "common/config-file.h"
#include "common/config-manager.h"
#include "common/savefile.h"
@@ -35,7 +36,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
@@ -95,14 +95,9 @@ void ScummEngine_v80he::o80_getFileSize() {
Common::SeekableReadStream *f = 0;
if (!_saveFileMan->listSavefiles(filename).empty()) {
- f = _saveFileMan->openForLoading((const char *)filename);
+ f = _saveFileMan->openForLoading(filename);
} else {
- Common::File *file = new Common::File();
- file->open((const char *)filename);
- if (!file->isOpen())
- delete file;
- else
- f = file;
+ f = SearchMan.createReadStreamForMember(filename);
}
if (!f) {
diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp
index 6acc16a804..841eba960d 100644
--- a/engines/scumm/he/script_v90he.cpp
+++ b/engines/scumm/he/script_v90he.cpp
@@ -32,7 +32,6 @@
#include "scumm/he/logic_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/he/sprite_he.h"
@@ -1948,42 +1947,51 @@ void ScummEngine_v90he::getArrayDim(int array, int *dim2start, int *dim2end, int
}
}
+static int sortArrayOffset;
+
static int compareByteArray(const void *a, const void *b) {
- int va = *((const uint8 *)a);
- int vb = *((const uint8 *)a);
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)b + sortArrayOffset);
return va - vb;
}
static int compareByteArrayReverse(const void *a, const void *b) {
- int va = *((const uint8 *)a);
- int vb = *((const uint8 *)a);
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)b + sortArrayOffset);
return vb - va;
}
static int compareIntArray(const void *a, const void *b) {
- int va = (int16)READ_LE_UINT16((const uint8 *)a);
- int vb = (int16)READ_LE_UINT16((const uint8 *)b);
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
return va - vb;
}
static int compareIntArrayReverse(const void *a, const void *b) {
- int va = (int16)READ_LE_UINT16((const uint8 *)a);
- int vb = (int16)READ_LE_UINT16((const uint8 *)b);
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
return vb - va;
}
static int compareDwordArray(const void *a, const void *b) {
- int va = (int32)READ_LE_UINT32((const uint8 *)a);
- int vb = (int32)READ_LE_UINT32((const uint8 *)b);
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
return va - vb;
}
static int compareDwordArrayReverse(const void *a, const void *b) {
- int va = (int32)READ_LE_UINT32((const uint8 *)a);
- int vb = (int32)READ_LE_UINT32((const uint8 *)b);
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
return vb - va;
}
+
+/**
+ * Sort a row range in a two-dimensional array by the value in a given column.
+ *
+ * We sort the data in the row range [dim2start..dim2end], according to the value
+ * in column dim1start == dim1end.
+ */
void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder) {
debug(9, "sortArray(%d, [%d,%d,%d,%d], %d)", array, dim2start, dim2end, dim1start, dim1end, sortOrder);
@@ -1992,11 +2000,21 @@ void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
assert(ah);
- const int num = dim2end - dim2start + 1;
- const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
- const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start))
- + dim1start - FROM_LE_32(ah->dim1start);
-
+ const int num = dim2end - dim2start + 1; // number of rows to sort
+ const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1; // length of a row = number of columns in it
+ const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start)); // memory offset to the first row to be sorted
+ sortArrayOffset = dim1start - FROM_LE_32(ah->dim1start); // offset to the column by which we sort
+
+ // Now we just have to invoke qsort on the appropriate row range. We
+ // need to pass sortArrayOffset as an implicit parameter to the
+ // comparison functions, which makes it necessary to use a global
+ // (albeit local to this file) variable.
+ // This could be avoided by using qsort_r or a self-written portable
+ // analog (this function passes an additional, user determined
+ // parameter to the comparison function).
+ // Another idea would be to use Common::sort, but that only is
+ // suitable if you sort objects of fixed size, which must be known
+ // during compilation time; clearly this not the case here.
switch (FROM_LE_32(ah->type)) {
case kByteArray:
case kStringArray:
@@ -2039,7 +2057,6 @@ void ScummEngine_v90he::o90_sortArray() {
int dim2end = pop();
int dim2start = pop();
getArrayDim(array, &dim2start, &dim2end, &dim1start, &dim1end);
- checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
sortArray(array, dim2start, dim2end, dim1start, dim1end, sortOrder);
}
break;
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 314697869c..6feeb2bb8f 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -32,6 +32,7 @@
#include "scumm/util.h"
#include "common/config-manager.h"
+#include "common/memstream.h"
#include "common/timer.h"
#include "common/util.h"
@@ -240,7 +241,7 @@ int SoundHE::isSoundCodeUsed(int sound) {
chan = i;
}
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) {
+ if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
return _heChannel[chan].sbngBlock;
} else {
return 0;
@@ -254,7 +255,7 @@ int SoundHE::getSoundPos(int sound) {
chan = i;
}
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) {
+ if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
int time = _vm->getHETimer(chan + 4) * _heChannel[chan].rate / 1000;
return time;
} else {
@@ -275,7 +276,7 @@ int SoundHE::getSoundVar(int sound, int var) {
chan = i;
}
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) {
+ if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
debug(5, "getSoundVar: sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]);
return _heChannel[chan].soundVars[var];
} else {
diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
index 361a3bc165..807380272d 100644
--- a/engines/scumm/he/wiz_he.cpp
+++ b/engines/scumm/he/wiz_he.cpp
@@ -25,6 +25,7 @@
#ifdef ENABLE_HE
+#include "common/archive.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "graphics/primitives.h"
@@ -358,6 +359,7 @@ static bool calcClipRects(int dst_w, int dst_h, int src_x, int src_y, int src_w,
void Wiz::writeColor(uint8 *dstPtr, int dstType, uint16 color) {
switch (dstType) {
+ case kDstCursor:
case kDstScreen:
WRITE_UINT16(dstPtr, color);
break;
@@ -1519,7 +1521,7 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int
cw = width;
ch = height;
dstPitch = cw * _vm->_bytesPerPixel;
- dstType = kDstMemory;
+ dstType = (_cursorImage) ? kDstCursor : kDstMemory;
} else {
if (dstResNum) {
uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum);
@@ -2373,12 +2375,7 @@ void Wiz::processWizImage(const WizParameters *params) {
if (!_vm->_saveFileMan->listSavefiles(filename).empty()) {
f = _vm->_saveFileMan->openForLoading(filename);
} else {
- Common::File *nf = new Common::File();
- nf->open(filename);
- if (!nf->isOpen())
- delete nf;
- else
- f = nf;
+ f = SearchMan.createReadStreamForMember(filename);
}
if (f) {
diff --git a/engines/scumm/he/wiz_he.h b/engines/scumm/he/wiz_he.h
index 1fa9564486..c255e27d14 100644
--- a/engines/scumm/he/wiz_he.h
+++ b/engines/scumm/he/wiz_he.h
@@ -145,7 +145,8 @@ enum {
enum DstSurface {
kDstScreen = 0,
kDstMemory = 1,
- kDstResource = 2
+ kDstResource = 2,
+ kDstCursor = 3
};
class ScummEngine_v71he;
diff --git a/engines/scumm/help.cpp b/engines/scumm/help.cpp
index e15c4a5592..f7a1c81da5 100644
--- a/engines/scumm/help.cpp
+++ b/engines/scumm/help.cpp
@@ -30,6 +30,7 @@
#include "scumm/help.h"
#include "scumm/scumm.h"
+#include "common/translation.h"
namespace Scumm {
@@ -72,216 +73,216 @@ void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platfo
int i = 0;
switch (page) {
case 1:
- title = "Common keyboard commands:";
- ADD_BIND("F5", "Save / Load dialog");
+ title = _("Common keyboard commands:");
+ ADD_BIND("F5", _("Save / Load dialog"));
if (version >= 5)
- ADD_BIND(".", "Skip line of text");
- ADD_BIND("Esc", "Skip cutscene");
- ADD_BIND("Space", "Pause game");
- ADD_BIND("Ctrl 0-9", "Load game state 1-10");
- ADD_BIND("Alt 0-9", "Save game state 1-10");
+ ADD_BIND(".", _("Skip line of text"));
+ ADD_BIND(_("Esc"), _("Skip cutscene"));
+ ADD_BIND(_("Space"), _("Pause game"));
+ ADD_BIND("Ctrl 0-9", _("Load game state 1-10"));
+ ADD_BIND("Alt 0-9", _("Save game state 1-10"));
#ifdef MACOSX
- ADD_BIND("Cmd q", "Quit");
+ ADD_BIND("Cmd q", _("Quit"));
#else
- ADD_BIND("Alt x, Ctrl z", "Quit");
+ ADD_BIND("Alt x, Ctrl z", _("Quit"));
#endif
- ADD_BIND("Alt Enter", "Toggle fullscreen");
- ADD_BIND("[, ]", "Music volume up / down");
- ADD_BIND("-, +", "Text speed slower / faster");
- ADD_BIND("Enter", "Simulate left mouse button");
- ADD_BIND("Tab", "Simulate right mouse button");
+ ADD_BIND(_("Alt Enter"), _("Toggle fullscreen"));
+ ADD_BIND("[, ]", _("Music volume up / down"));
+ ADD_BIND("-, +", _("Text speed slower / faster"));
+ ADD_BIND(_("Enter"), _("Simulate left mouse button"));
+ ADD_BIND(_("Tab"), _("Simulate right mouse button"));
break;
case 2:
- title = "Special keyboard commands:";
- ADD_BIND("~, #", "Show / Hide console");
- ADD_BIND("Ctrl d", "Start the debugger");
- ADD_BIND("Ctrl s", "Show memory consumption");
- ADD_BIND("Ctrl f", "Run in fast mode (*)");
- ADD_BIND("Ctrl g", "Run in really fast mode (*)");
- ADD_BIND("Ctrl m", "Toggle mouse capture");
- ADD_BIND("Ctrl Alt 1-8", "Switch between graphics filters");
- ADD_BIND("Ctrl Alt +, -", "Increase / Decrease scale factor");
- ADD_BIND("Ctrl Alt a", "Toggle aspect-ratio correction");
+ title = _("Special keyboard commands:");
+ ADD_BIND("~, #", _("Show / Hide console"));
+ ADD_BIND("Ctrl d", _("Start the debugger"));
+ ADD_BIND("Ctrl s", _("Show memory consumption"));
+ ADD_BIND("Ctrl f", _("Run in fast mode (*)"));
+ ADD_BIND("Ctrl g", _("Run in really fast mode (*)"));
+ ADD_BIND("Ctrl m", _("Toggle mouse capture"));
+ ADD_BIND("Ctrl Alt 1-8", _("Switch between graphics filters"));
+ ADD_BIND("Ctrl Alt +, -", _("Increase / Decrease scale factor"));
+ ADD_BIND("Ctrl Alt a", _("Toggle aspect-ratio correction"));
ADD_LINE;
ADD_LINE;
// FIXME: This should use word-wrapping, and should not assume
// that the font is mono-spaced.
- ADD_TEXT("* Note that using ctrl-f and");
- ADD_TEXT(" ctrl-g are not recommended");
- ADD_TEXT(" since they may cause crashes");
- ADD_TEXT(" or incorrect game behaviour.");
+ ADD_TEXT(_("* Note that using ctrl-f and"));
+ ADD_TEXT(_(" ctrl-g are not recommended"));
+ ADD_TEXT(_(" since they may cause crashes"));
+ ADD_TEXT(_(" or incorrect game behaviour."));
break;
case 3:
if (gameId == GID_LOOM)
- title = "Spinning drafts on the keyboard:";
+ title = _("Spinning drafts on the keyboard:");
else
- title = "Main game controls:";
+ title = _("Main game controls:");
switch (gameId) {
case GID_ZAK:
case GID_MANIAC:
if (platform == Common::kPlatformNES) {
- ADD_BIND("q", "Push");
- ADD_BIND("a", "Pull");
- ADD_BIND("z", "Give");
- ADD_BIND("w", "Open");
- ADD_BIND("s", "Close");
- ADD_BIND("x", "Go to");
- ADD_BIND("e", "Get");
- ADD_BIND("d", "Use");
- ADD_BIND("c", "Read");
- ADD_BIND("r", "New kid");
- ADD_BIND("f", "Turn on");
- ADD_BIND("v", "Turn off");
+ ADD_BIND("q", _("Push"));
+ ADD_BIND("a", _("Pull"));
+ ADD_BIND("z", _("Give"));
+ ADD_BIND("w", _("Open"));
+ ADD_BIND("s", _("Close"));
+ ADD_BIND("x", _("Go to"));
+ ADD_BIND("e", _("Get"));
+ ADD_BIND("d", _("Use"));
+ ADD_BIND("c", _("Read"));
+ ADD_BIND("r", _("New kid"));
+ ADD_BIND("f", _("Turn on"));
+ ADD_BIND("v", _("Turn off"));
break;
}
- ADD_BIND("q", "Push");
- ADD_BIND("a", "Pull");
- ADD_BIND("z", "Give");
- ADD_BIND("w", "Open");
- ADD_BIND("s", "Close");
- ADD_BIND("x", "Read");
- ADD_BIND("e", "Walk to");
- ADD_BIND("d", "Pick up");
- ADD_BIND("c", "What is");
+ ADD_BIND("q", _("Push"));
+ ADD_BIND("a", _("Pull"));
+ ADD_BIND("z", _("Give"));
+ ADD_BIND("w", _("Open"));
+ ADD_BIND("s", _("Close"));
+ ADD_BIND("x", _("Read"));
+ ADD_BIND("e", _("Walk to"));
+ ADD_BIND("d", _("Pick up"));
+ ADD_BIND("c", _("What is"));
if (gameId == GID_MANIAC) {
- ADD_BIND("r", "Unlock");
- ADD_BIND("f", "New kid");
+ ADD_BIND("r", _("Unlock"));
+ ADD_BIND("f", _("New kid"));
} else {
- ADD_BIND("r", "Put on");
- ADD_BIND("f", "Take off");
+ ADD_BIND("r", _("Put on"));
+ ADD_BIND("f", _("Take off"));
}
- ADD_BIND("v", "Use");
- ADD_BIND("t", "Turn on");
- ADD_BIND("g", "Turn off");
+ ADD_BIND("v", _("Use"));
+ ADD_BIND("t", _("Turn on"));
+ ADD_BIND("g", _("Turn off"));
if (gameId == GID_MANIAC)
- ADD_BIND("b", "Fix");
+ ADD_BIND("b", _("Fix"));
else
- ADD_BIND("b", "Switch");
+ ADD_BIND("b", _("Switch"));
break;
case GID_INDY3:
- ADD_BIND("q", "Push");
- ADD_BIND("a", "Pull");
- ADD_BIND("z", "Give");
- ADD_BIND("w", "Open");
- ADD_BIND("s", "Close");
- ADD_BIND("x", "Look");
- ADD_BIND("e", "Walk to");
- ADD_BIND("d", "Pick up");
- ADD_BIND("c", "What is");
- ADD_BIND("r", "Use");
- ADD_BIND("f", "Turn on");
- ADD_BIND("v", "Turn off");
- ADD_BIND("t", "Talk");
- ADD_BIND("g", "Travel");
- ADD_BIND("b", "To Henry / To Indy");
+ ADD_BIND("q", _("Push"));
+ ADD_BIND("a", _("Pull"));
+ ADD_BIND("z", _("Give"));
+ ADD_BIND("w", _("Open"));
+ ADD_BIND("s", _("Close"));
+ ADD_BIND("x", _("Look"));
+ ADD_BIND("e", _("Walk to"));
+ ADD_BIND("d", _("Pick up"));
+ ADD_BIND("c", _("What is"));
+ ADD_BIND("r", _("Use"));
+ ADD_BIND("f", _("Turn on"));
+ ADD_BIND("v", _("Turn off"));
+ ADD_BIND("t", _("Talk"));
+ ADD_BIND("g", _("Travel"));
+ ADD_BIND("b", _("To Henry / To Indy"));
break;
case GID_LOOM:
- ADD_BIND("q, c", "play C minor on distaff");
- ADD_BIND("w, d", "play D on distaff");
- ADD_BIND("e, e", "play E on distaff");
- ADD_BIND("r, f", "play F on distaff");
- ADD_BIND("t, g", "play G on distaff");
- ADD_BIND("y, a", "play A on distaff");
- ADD_BIND("u, b", "play B on distaff");
- ADD_BIND("i, C", "play C major on distaff");
+ ADD_BIND("q, c", _("play C minor on distaff"));
+ ADD_BIND("w, d", _("play D on distaff"));
+ ADD_BIND("e, e", _("play E on distaff"));
+ ADD_BIND("r, f", _("play F on distaff"));
+ ADD_BIND("t, g", _("play G on distaff"));
+ ADD_BIND("y, a", _("play A on distaff"));
+ ADD_BIND("u, b", _("play B on distaff"));
+ ADD_BIND("i, C", _("play C major on distaff"));
break;
case GID_MONKEY_EGA:
case GID_MONKEY_VGA:
- ADD_BIND("o", "Open");
- ADD_BIND("c", "Close");
- ADD_BIND("s", "puSh");
- ADD_BIND("y", "pull (Yank)");
- ADD_BIND("w", "Walk to");
- ADD_BIND("p", "Pick up");
- ADD_BIND("t", "Talk to");
- ADD_BIND("g", "Give");
- ADD_BIND("u", "Use");
- ADD_BIND("l", "Look at");
- ADD_BIND("n", "turn oN");
- ADD_BIND("f", "turn oFf");
+ ADD_BIND("o", _("Open"));
+ ADD_BIND("c", _("Close"));
+ ADD_BIND("s", _("puSh"));
+ ADD_BIND("y", _("pull (Yank)"));
+ ADD_BIND("w", _("Walk to"));
+ ADD_BIND("p", _("Pick up"));
+ ADD_BIND("t", _("Talk to"));
+ ADD_BIND("g", _("Give"));
+ ADD_BIND("u", _("Use"));
+ ADD_BIND("l", _("Look at"));
+ ADD_BIND("n", _("turn oN"));
+ ADD_BIND("f", _("turn oFf"));
break;
case GID_MONKEY:
case GID_MONKEY2:
case GID_INDY4:
case GID_TENTACLE:
- ADD_BIND("g", "Give");
- ADD_BIND("o", "Open");
- ADD_BIND("c", "Close");
- ADD_BIND("p", "Pick up");
- ADD_BIND("l", "Look at");
- ADD_BIND("t", "Talk to");
- ADD_BIND("u", "Use");
- ADD_BIND("s", "puSh");
- ADD_BIND("y", "pull (Yank)");
+ ADD_BIND("g", _("Give"));
+ ADD_BIND("o", _("Open"));
+ ADD_BIND("c", _("Close"));
+ ADD_BIND("p", _("Pick up"));
+ ADD_BIND("l", _("Look at"));
+ ADD_BIND("t", _("Talk to"));
+ ADD_BIND("u", _("Use"));
+ ADD_BIND("s", _("puSh"));
+ ADD_BIND("y", _("pull (Yank)"));
if (platform == Common::kPlatformSegaCD) {
- ADD_BIND("KeyUp", "Highlight prev dialogue");
- ADD_BIND("KeyDown", "Highlight next dialogue");
+ ADD_BIND(_("KeyUp"), _("Highlight prev dialogue"));
+ ADD_BIND(_("KeyDown"), _("Highlight next dialogue"));
}
break;
case GID_SAMNMAX:
- ADD_BIND("w", "Walk");
- ADD_BIND("t", "Talk");
- ADD_BIND("u", "Use");
- ADD_BIND("i", "Inventory");
- ADD_BIND("o", "Object");
- ADD_BIND("p", "Pick up");
- ADD_BIND("l", "Look");
- ADD_BIND("b", "Black and White / Color");
+ ADD_BIND("w", _("Walk"));
+ ADD_BIND("t", _("Talk"));
+ ADD_BIND("u", _("Use"));
+ ADD_BIND("i", _("Inventory"));
+ ADD_BIND("o", _("Object"));
+ ADD_BIND("p", _("Pick up"));
+ ADD_BIND("l", _("Look"));
+ ADD_BIND("b", _("Black and White / Color"));
break;
case GID_FT:
- ADD_BIND("e", "Eyes");
- ADD_BIND("t", "Tongue");
- ADD_BIND("i", "Inventory");
- ADD_BIND("p", "Punch");
- ADD_BIND("k", "Kick");
+ ADD_BIND("e", _("Eyes"));
+ ADD_BIND("t", _("Tongue"));
+ ADD_BIND("i", _("Inventory"));
+ ADD_BIND("p", _("Punch"));
+ ADD_BIND("k", _("Kick"));
break;
case GID_DIG:
- ADD_BIND("e", "Examine");
- ADD_BIND("t", "Regular cursor");
- ADD_BIND("i", "Inventory");
- ADD_BIND("c", "Comm");
+ ADD_BIND("e", _("Examine"));
+ ADD_BIND("t", _("Regular cursor"));
+ ADD_BIND("i", _("Inventory"));
+ ADD_BIND("c", _("Comm"));
break;
case GID_CMI:
- ADD_BIND("F1", "Save / Load / Options");
- ADD_BIND("e", "Examine");
- ADD_BIND("t", "Talk to");
- ADD_BIND("i", "Inventory");
- ADD_BIND("u", "Use");
+ ADD_BIND("F1", _("Save / Load / Options"));
+ ADD_BIND("e", _("Examine"));
+ ADD_BIND("t", _("Talk to"));
+ ADD_BIND("i", _("Inventory"));
+ ADD_BIND("u", _("Use"));
break;
}
break;
case 4:
- title = "Other game controls:";
+ title = _("Other game controls:");
if (version <= 2) {
- ADD_TEXT("Inventory:");
- ADD_BIND("u", "Scroll list up");
- ADD_BIND("j", "Scroll list down");
- ADD_BIND("i", "Upper left item");
- ADD_BIND("k", "Lower left item");
- ADD_BIND("o", "Upper right item");
- ADD_BIND("l", "Lower right item");
+ ADD_TEXT(_("Inventory:"));
+ ADD_BIND("u", _("Scroll list up"));
+ ADD_BIND("j", _("Scroll list down"));
+ ADD_BIND("i", _("Upper left item"));
+ ADD_BIND("k", _("Lower left item"));
+ ADD_BIND("o", _("Upper right item"));
+ ADD_BIND("l", _("Lower right item"));
ADD_LINE;
} else if (gameId == GID_INDY3 || gameId == GID_ZAK) {
// Indy3, or FM-TOWNS Zak
- ADD_TEXT("Inventory:");
- ADD_BIND("y", "Upper left item");
- ADD_BIND("h", "Middle left item");
- ADD_BIND("n", "Lower left item");
- ADD_BIND("u", "Upper right item");
- ADD_BIND("j", "Middle right item");
- ADD_BIND("m", "Lower right item");
- ADD_BIND("o", "Scroll list up");
- ADD_BIND("l", "Scroll list down");
+ ADD_TEXT(_("Inventory:"));
+ ADD_BIND("y", _("Upper left item"));
+ ADD_BIND("h", _("Middle left item"));
+ ADD_BIND("n", _("Lower left item"));
+ ADD_BIND("u", _("Upper right item"));
+ ADD_BIND("j", _("Middle right item"));
+ ADD_BIND("m", _("Lower right item"));
+ ADD_BIND("o", _("Scroll list up"));
+ ADD_BIND("l", _("Scroll list down"));
ADD_LINE;
}
if (gameId == GID_MANIAC) {
- ADD_TEXT("Switching characters:");
+ ADD_TEXT(_("Switching characters:"));
ADD_BIND("F1", "Dave");
- ADD_BIND("F2", "Second kid");
- ADD_BIND("F3", "Third kid");
+ ADD_BIND("F2", _("Second kid"));
+ ADD_BIND("F3", _("Third kid"));
} else if (gameId == GID_ZAK) {
- ADD_TEXT("Switching characters:");
+ ADD_TEXT(_("Switching characters:"));
ADD_BIND("F1", "Zak");
ADD_BIND("F2", "Annie");
ADD_BIND("F3", "Melissa");
@@ -291,38 +292,38 @@ void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platfo
case 5:
switch (gameId) {
case GID_INDY3:
- title = "Fighting controls (numpad):";
- ADD_BIND("7", "Step back");
- ADD_BIND("4", "Step back");
- ADD_BIND("1", "Step back");
- ADD_BIND("8", "Block high");
- ADD_BIND("5", "Block middle");
- ADD_BIND("2", "Block low");
- ADD_BIND("9", "Punch high");
- ADD_BIND("6", "Punch middle");
- ADD_BIND("3", "Punch low");
+ title = _("Fighting controls (numpad):");
+ ADD_BIND("7", _("Step back"));
+ ADD_BIND("4", _("Step back"));
+ ADD_BIND("1", _("Step back"));
+ ADD_BIND("8", _("Block high"));
+ ADD_BIND("5", _("Block middle"));
+ ADD_BIND("2", _("Block low"));
+ ADD_BIND("9", _("Punch high"));
+ ADD_BIND("6", _("Punch middle"));
+ ADD_BIND("3", _("Punch low"));
ADD_LINE;
ADD_LINE;
- ADD_TEXT("These are for Indy on left.");
- ADD_TEXT("When Indy is on the right,");
- ADD_TEXT("7, 4, and 1 are switched with");
- ADD_TEXT("9, 6, and 3, respectively.");
+ ADD_TEXT(_("These are for Indy on left."));
+ ADD_TEXT(_("When Indy is on the right,"));
+ ADD_TEXT(_("7, 4, and 1 are switched with"));
+ ADD_TEXT(_("9, 6, and 3, respectively."));
break;
}
break;
case 6:
switch (gameId) {
case GID_INDY3:
- title = "Biplane controls (numpad):";
- ADD_BIND("7", "Fly to upper left");
- ADD_BIND("4", "Fly to left");
- ADD_BIND("1", "Fly to lower left");
- ADD_BIND("8", "Fly upwards");
- ADD_BIND("5", "Fly straight");
- ADD_BIND("2", "Fly down");
- ADD_BIND("9", "Fly to upper right");
- ADD_BIND("6", "Fly to right");
- ADD_BIND("3", "Fly to lower right");
+ title = _("Biplane controls (numpad):");
+ ADD_BIND("7", _("Fly to upper left"));
+ ADD_BIND("4", _("Fly to left"));
+ ADD_BIND("1", _("Fly to lower left"));
+ ADD_BIND("8", _("Fly upwards"));
+ ADD_BIND("5", _("Fly straight"));
+ ADD_BIND("2", _("Fly down"));
+ ADD_BIND("9", _("Fly to upper right"));
+ ADD_BIND("6", _("Fly to right"));
+ ADD_BIND("3", _("Fly to lower right"));
break;
}
break;
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 3a672d57b5..8cccbc227a 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -700,9 +700,9 @@ int32 IMuseInternal::doCommand_internal(int numargs, int a[]) {
{
Common::String string = "doCommand - ";
- string += Common::String::printf("%d (%d/%d)", a[0], (int)param, (int)cmd);
+ string += Common::String::format("%d (%d/%d)", a[0], (int)param, (int)cmd);
for (i = 1; i < numargs; ++i)
- string += Common::String::printf(", %d", a[i]);
+ string += Common::String::format(", %d", a[i]);
debugC(DEBUG_IMUSE, "%s", string.c_str());
}
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
index 3ae6f7e919..f10f17f3d8 100644
--- a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
@@ -157,7 +157,7 @@ BundleMgr::~BundleMgr() {
delete _file;
}
-Common::File *BundleMgr::getFile(const char *filename, int32 &offset, int32 &size) {
+Common::SeekableReadStream *BundleMgr::getFile(const char *filename, int32 &offset, int32 &size) {
BundleDirCache::IndexNode target;
strcpy(target.filename, filename);
BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.h b/engines/scumm/imuse_digi/dimuse_bndmgr.h
index 65360d8ba4..a78697a854 100644
--- a/engines/scumm/imuse_digi/dimuse_bndmgr.h
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.h
@@ -101,7 +101,7 @@ public:
bool open(const char *filename, bool &compressed, bool errorFlag = false);
void close();
- Common::File *getFile(const char *filename, int32 &offset, int32 &size);
+ Common::SeekableReadStream *getFile(const char *filename, int32 &offset, int32 &size);
int32 decompressSampleByName(const char *name, int32 offset, int32 size, byte **compFinal, bool headerOutside);
int32 decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **compFinal, int header_size, bool headerOutside);
int32 decompressSampleByCurIndex(int32 offset, int32 size, byte **compFinal, int headerSize, bool headerOutside);
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
index 24cac8707b..4054843163 100644
--- a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
@@ -93,7 +93,7 @@ void ImuseDigiSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps, i
} while (tag != MKID_BE('DATA'));
}
-void ImuseDigiSndMgr::prepareSoundFromRMAP(Common::File *file, SoundDesc *sound, int32 offset, int32 size) {
+void ImuseDigiSndMgr::prepareSoundFromRMAP(Common::SeekableReadStream *file, SoundDesc *sound, int32 offset, int32 size) {
int l;
file->seek(offset, SEEK_SET);
@@ -428,7 +428,7 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char
char fileName[24];
int32 offset = 0, size = 0;
sprintf(fileName, "%s.map", soundName);
- Common::File *rmapFile = sound->bundle->getFile(fileName, offset, size);
+ Common::SeekableReadStream *rmapFile = sound->bundle->getFile(fileName, offset, size);
if (!rmapFile) {
closeSound(sound);
return NULL;
@@ -666,7 +666,7 @@ int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte
sprintf(fileName, "%s_reg%03d", soundDesc->name, region);
if (scumm_stricmp(fileName, soundDesc->lastFileName) != 0) {
int32 offs = 0, len = 0;
- Common::File *cmpFile;
+ Common::SeekableReadStream *cmpFile;
uint8 soundMode = 0;
sprintf(fileName, "%s_reg%03d.fla", soundDesc->name, region);
@@ -700,7 +700,7 @@ int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte
assert(len);
if (!soundDesc->compressedStream) {
- Common::MemoryReadStream *tmp = cmpFile->readStream(len);
+ Common::SeekableReadStream *tmp = cmpFile->readStream(len);
assert(tmp);
#ifdef USE_FLAC
if (soundMode == 3)
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.h b/engines/scumm/imuse_digi/dimuse_sndmgr.h
index 0a667eba9d..aa7a1b77ca 100644
--- a/engines/scumm/imuse_digi/dimuse_sndmgr.h
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.h
@@ -113,7 +113,7 @@ private:
bool checkForProperHandle(SoundDesc *soundDesc);
SoundDesc *allocSlot();
void prepareSound(byte *ptr, SoundDesc *sound);
- void prepareSoundFromRMAP(Common::File *file, SoundDesc *sound, int32 offset, int32 size);
+ void prepareSoundFromRMAP(Common::SeekableReadStream *file, SoundDesc *sound, int32 offset, int32 size);
ScummEngine *_vm;
byte _disk;
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index dc3a5d26b3..ab5357f1dd 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -28,7 +28,7 @@
#include "common/system.h"
#include "gui/message.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
#include "scumm/debugger.h"
#include "scumm/dialogs.h"
diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index 3876bd4e80..f2e50382b3 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -944,7 +944,7 @@ bool Insane::actor1StateFlags(int state) {
bool retvalue = 0;
unsigned int i;
- for (i = 0; i < sizeof(spans); i++) {
+ for (i = 0; i < ARRAYSIZE(spans); i++) {
retvalue = !retvalue;
if (spans[i] <= state)
break;
@@ -1099,7 +1099,7 @@ bool Insane::actor0StateFlags1(int state) {
bool retvalue = 1;
unsigned int i;
- for (i = 0; i < sizeof(spans); i++) {
+ for (i = 0; i < ARRAYSIZE(spans); i++) {
retvalue = !retvalue;
if (spans[i] >= state)
break;
@@ -1119,7 +1119,7 @@ bool Insane::actor0StateFlags2(int state) {
bool retvalue = 1;
unsigned int i;
- for (i = 0; i < sizeof(spans); i++) {
+ for (i = 0; i < ARRAYSIZE(spans); i++) {
retvalue = !retvalue;
if (spans[i] >= state)
break;
diff --git a/engines/scumm/insane/insane_ben.cpp b/engines/scumm/insane/insane_ben.cpp
index 9ddb4c6670..05775f1585 100644
--- a/engines/scumm/insane/insane_ben.cpp
+++ b/engines/scumm/insane/insane_ben.cpp
@@ -1163,7 +1163,7 @@ void Insane::actor02Reaction(int32 buttons) {
setBenState();
_actor[0].act[2].tilt = 0;
// for some reason there is no break at this
- // place, so tilt gets overriden on next line
+ // place, so tilt gets overridden on next line
}
_actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
break;
diff --git a/engines/scumm/insane/insane_enemy.cpp b/engines/scumm/insane/insane_enemy.cpp
index 2291b2c37b..e8d97d3875 100644
--- a/engines/scumm/insane/insane_enemy.cpp
+++ b/engines/scumm/insane/insane_enemy.cpp
@@ -1328,7 +1328,7 @@ void Insane::turnEnemy(bool battle) {
if (_actor[1].damage < _actor[1].maxdamage) {
_actor[1].lost = false;
} else {
- if (!_actor[1].lost && !_actor[1].lost) {
+ if (!_actor[1].lost && !_actor[0].lost) {
_actor[1].lost = true;
_actor[1].act[2].state = 36;
_actor[1].act[1].state = 36;
@@ -2072,7 +2072,7 @@ void Insane::actor12Reaction(int32 buttons) {
setEnemyState();
_actor[1].act[2].tilt = 0;
// for some reason there is no break at this
- // place, so tilt gets overriden on next line
+ // place, so tilt gets overridden on next line
}
_actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
break;
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index cd89f5ffad..1a60564a9e 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -41,6 +41,7 @@ MODULE_OBJS := \
player_v1.o \
player_v2.o \
player_v2a.o \
+ player_v2base.o \
player_v2cms.o \
player_v3a.o \
player_v4a.o \
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index e2b68f8d3b..c44043ca81 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -315,6 +315,10 @@ int ScummEngine::getObjectIndex(int object) const {
return -1;
for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_game.version == 0 )
+ if( _objs[i].flags != _v0ObjectFlag )
+ continue;
+
if (_objs[i].obj_nr == object)
return i;
}
@@ -526,6 +530,9 @@ int ScummEngine::findObject(int x, int y) {
#endif
if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
_objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) {
+ // MMC64: Set the object search flag
+ if (_game.version == 0)
+ _v0ObjectFlag = _objs[i].flags;
if (_game.version == 0 && _v0ObjectIndex)
return i;
else
@@ -993,6 +1000,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *
od->flags = Gdi::dbAllowMaskOr;
if (_game.version == 8) {
+ assert(imhd);
od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
od->parent = cdhd->v7.parent;
@@ -1008,6 +1016,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *
od->flags = ((((byte)READ_LE_UINT32(&imhd->v8.flags)) & 16) == 0) ? Gdi::dbAllowMaskOr : 0;
} else if (_game.version == 7) {
+ assert(imhd);
od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
od->parent = cdhd->v7.parent;
@@ -1020,6 +1029,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *
od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir);
} else if (_game.version == 6) {
+ assert(imhd);
od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id));
od->width = READ_LE_UINT16(&cdhd->v6.w);
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 5c0c58595b..509547984a 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -71,7 +71,7 @@ void ScummEngine::resetPalette() {
// Use 17 color table for v1 games to allow correct color for inventory and
// sentence line. Original games used some kind of dynamic color table
// remapping between rooms.
- 0xFF, 0x55, 0xFF
+ 0x7F, 0x3B, 0xA6
};
static const byte tableNESPalette[] = {
@@ -498,7 +498,7 @@ void ScummEngine::cyclePalette() {
int i, j;
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1))
+ if (_game.platform == Common::kPlatformFMTowns && !(_townsPaletteFlags & 1))
return;
#endif
@@ -544,7 +544,7 @@ void ScummEngine::moveMemInPalRes(int start, int end, byte direction) {
void ScummEngine::palManipulateInit(int resID, int start, int end, int time) {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
- if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1))
+ if (_game.platform == Common::kPlatformFMTowns && !(_townsPaletteFlags & 1))
return;
#endif
@@ -1092,7 +1092,7 @@ void ScummEngine::updatePalette() {
else
data = _currentPalette + i * 3;
- // Sam & Max film noir mode. Convert the colours to grayscale
+ // Sam & Max film noir mode. Convert the colors to grayscale
// before uploading them to the backend.
if (noir_mode) {
diff --git a/engines/scumm/player_nes.h b/engines/scumm/player_nes.h
index b2eafb79b0..89292f7c24 100644
--- a/engines/scumm/player_nes.h
+++ b/engines/scumm/player_nes.h
@@ -75,8 +75,6 @@ private:
void APU_writeControl(byte value);
byte APU_readStatus();
- void do_mix(int16 *buf, uint len);
-
ScummEngine *_vm;
Audio::Mixer *_mixer;
Audio::SoundHandle _soundHandle;
diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/player_pce.cpp
index a17aab4d59..4236fb2d6b 100644
--- a/engines/scumm/player_pce.cpp
+++ b/engines/scumm/player_pce.cpp
@@ -152,7 +152,7 @@ static const uint16 sounds[13][6] = {
// 0xB2A1
static const byte data_table[482] = {
/* 0*/ 0xE2, 0x0A, 0xE1, 0x0D, 0xE6, 0xED, 0xE0, 0x0F, 0xE2, 0x00, 0xE1, 0x00,
- 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2,
0xB2, 0xE1, 0x03, 0xF2, 0xF2, 0xB2, 0xE1, 0x04, 0xF2, 0xF2, 0xB2, 0xE1,
0x05, 0xF2, 0xF2, 0xB2, 0xE1, 0x06, 0xF2, 0xF2, 0xB2, 0xE1, 0x07, 0xF2,
0xF2, 0xB2, 0xE1, 0x08, 0xF2, 0xF2, 0xB2, 0xE1, 0x09, 0xF2, 0xF2, 0xB2,
@@ -208,10 +208,10 @@ static const byte data_table[482] = {
/*395*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x0B, 0xE8, 0x0B, 0xFF,
/*408*/ 0xE2, 0x0C, 0xE1, 0x03, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF,
/*467*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x1B, 0xE8, 0x1B, 0xFF,
/*480*/ 0xFF,
@@ -238,11 +238,11 @@ private:
double _clock;
double _rate;
uint8 _select;
- uint8 _balance;
- channel_t _channel[8];
- int16 _volumeTable[32];
- uint32 _noiseFreqTable[32];
- uint32 _waveFreqTable[4096];
+ uint8 _balance;
+ channel_t _channel[8];
+ int16 _volumeTable[32];
+ uint32 _noiseFreqTable[32];
+ uint32 _waveFreqTable[4096];
public:
void init();
@@ -255,180 +255,180 @@ public:
PSG_HuC6280::PSG_HuC6280(double clock, double samplerate) {
_clock = clock;
- _rate = samplerate;
+ _rate = samplerate;
- // Initialize PSG_HuC6280 emulator
- init();
+ // Initialize PSG_HuC6280 emulator
+ init();
}
void PSG_HuC6280::init() {
- int i;
- double step;
+ int i;
+ double step;
- // Loudest volume level for table
- double level = 65535.0 / 6.0 / 32.0;
+ // Loudest volume level for table
+ double level = 65535.0 / 6.0 / 32.0;
- // Clear context
+ // Clear context
reset();
- // Make waveform frequency table
- for(i = 0; i < 4096; i++) {
- step = ((_clock / _rate) * 4096) / (i+1);
- _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step;
- }
-
- // Make noise frequency table
- for(i = 0; i < 32; i++) {
- step = ((_clock / _rate) * 32) / (i+1);
- _noiseFreqTable[i] = (uint32)step;
- }
-
- // Make volume table
- // PSG_HuC6280 has 48dB volume range spread over 32 steps
- step = 48.0 / 32.0;
- for(i = 0; i < 31; i++) {
- _volumeTable[i] = (uint16)level;
- level /= pow(10.0, step / 20.0);
- }
- _volumeTable[31] = 0;
+ // Make waveform frequency table
+ for(i = 0; i < 4096; i++) {
+ step = ((_clock / _rate) * 4096) / (i+1);
+ _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step;
+ }
+
+ // Make noise frequency table
+ for(i = 0; i < 32; i++) {
+ step = ((_clock / _rate) * 32) / (i+1);
+ _noiseFreqTable[i] = (uint32)step;
+ }
+
+ // Make volume table
+ // PSG_HuC6280 has 48dB volume range spread over 32 steps
+ step = 48.0 / 32.0;
+ for(i = 0; i < 31; i++) {
+ _volumeTable[i] = (uint16)level;
+ level /= pow(10.0, step / 20.0);
+ }
+ _volumeTable[31] = 0;
}
void PSG_HuC6280::reset() {
_select = 0;
- _balance = 0xFF;
- memset(_channel, 0, sizeof(_channel));
- memset(_volumeTable, 0, sizeof(_volumeTable));
- memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable));
- memset(_waveFreqTable, 0, sizeof(_waveFreqTable));
+ _balance = 0xFF;
+ memset(_channel, 0, sizeof(_channel));
+ memset(_volumeTable, 0, sizeof(_volumeTable));
+ memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable));
+ memset(_waveFreqTable, 0, sizeof(_waveFreqTable));
}
void PSG_HuC6280::write(int offset, byte data) {
- channel_t *chan = &_channel[_select];
-
- switch(offset & 0x0F) {
- case 0x00: // Channel select
- _select = data & 0x07;
- break;
-
- case 0x01: // Global balance
- _balance = data;
- break;
-
- case 0x02: // Channel frequency (LSB)
- chan->frequency = (chan->frequency & 0x0F00) | data;
- chan->frequency &= 0x0FFF;
- break;
-
- case 0x03: // Channel frequency (MSB)
- chan->frequency = (chan->frequency & 0x00FF) | (data << 8);
- chan->frequency &= 0x0FFF;
- break;
-
- case 0x04: // Channel control (key-on, DDA mode, volume)
- // 1-to-0 transition of DDA bit resets waveform index
- if((chan->control & 0x40) && ((data & 0x40) == 0)) {
- chan->index = 0;
- }
- chan->control = data;
- break;
-
- case 0x05: // Channel balance
- chan->balance = data;
- break;
-
- case 0x06: // Channel waveform data
- switch(chan->control & 0xC0) {
- case 0x00:
- chan->waveform[chan->index & 0x1F] = data & 0x1F;
- chan->index = (chan->index + 1) & 0x1F;
- break;
-
- case 0x40:
- break;
-
- case 0x80:
- chan->waveform[chan->index & 0x1F] = data & 0x1F;
- chan->index = (chan->index + 1) & 0x1F;
- break;
-
- case 0xC0:
- chan->dda = data & 0x1F;
- break;
- }
-
- break;
-
- case 0x07: // Noise control (enable, frequency)
- case 0x08: // LFO frequency
- case 0x09: // LFO control (enable, mode)
- break;
-
- default:
- break;
- }
+ channel_t *chan = &_channel[_select];
+
+ switch(offset & 0x0F) {
+ case 0x00: // Channel select
+ _select = data & 0x07;
+ break;
+
+ case 0x01: // Global balance
+ _balance = data;
+ break;
+
+ case 0x02: // Channel frequency (LSB)
+ chan->frequency = (chan->frequency & 0x0F00) | data;
+ chan->frequency &= 0x0FFF;
+ break;
+
+ case 0x03: // Channel frequency (MSB)
+ chan->frequency = (chan->frequency & 0x00FF) | (data << 8);
+ chan->frequency &= 0x0FFF;
+ break;
+
+ case 0x04: // Channel control (key-on, DDA mode, volume)
+ // 1-to-0 transition of DDA bit resets waveform index
+ if((chan->control & 0x40) && ((data & 0x40) == 0)) {
+ chan->index = 0;
+ }
+ chan->control = data;
+ break;
+
+ case 0x05: // Channel balance
+ chan->balance = data;
+ break;
+
+ case 0x06: // Channel waveform data
+ switch(chan->control & 0xC0) {
+ case 0x00:
+ chan->waveform[chan->index & 0x1F] = data & 0x1F;
+ chan->index = (chan->index + 1) & 0x1F;
+ break;
+
+ case 0x40:
+ break;
+
+ case 0x80:
+ chan->waveform[chan->index & 0x1F] = data & 0x1F;
+ chan->index = (chan->index + 1) & 0x1F;
+ break;
+
+ case 0xC0:
+ chan->dda = data & 0x1F;
+ break;
+ }
+
+ break;
+
+ case 0x07: // Noise control (enable, frequency)
+ case 0x08: // LFO frequency
+ case 0x09: // LFO control (enable, mode)
+ break;
+
+ default:
+ break;
+ }
}
void PSG_HuC6280::update(int16* samples, int sampleCnt) {
- static const int scale_tab[] = {
- 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
- 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
- };
- int ch;
- int i;
+ static const int scale_tab[] = {
+ 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
+ 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
+ };
+ int ch;
+ int i;
- int lmal = (_balance >> 4) & 0x0F;
- int rmal = (_balance >> 0) & 0x0F;
- int vll, vlr;
+ int lmal = (_balance >> 4) & 0x0F;
+ int rmal = (_balance >> 0) & 0x0F;
+ int vll, vlr;
- lmal = scale_tab[lmal];
- rmal = scale_tab[rmal];
+ lmal = scale_tab[lmal];
+ rmal = scale_tab[rmal];
- // Clear buffer
+ // Clear buffer
memset(samples, 0, 2 * sampleCnt * sizeof(int16));
- for(ch = 0; ch < 6; ch++) {
- // Only look at enabled channels
- if(_channel[ch].control & 0x80) {
- int lal = (_channel[ch].balance >> 4) & 0x0F;
- int ral = (_channel[ch].balance >> 0) & 0x0F;
- int al = _channel[ch].control & 0x1F;
-
- lal = scale_tab[lal];
- ral = scale_tab[ral];
-
- // Calculate volume just as the patent says
- vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
- if(vll > 0x1F) vll = 0x1F;
-
- vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
- if(vlr > 0x1F) vlr = 0x1F;
-
- vll = _volumeTable[vll];
- vlr = _volumeTable[vlr];
-
- // Check channel mode
- if(_channel[ch].control & 0x40) {
- /* DDA mode */
- for(i = 0; i < sampleCnt; i++) {
- samples[2*i] += (int16)(vll * (_channel[ch].dda - 16));
- samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16));
- }
- } else {
- /* Waveform mode */
- uint32 step = _waveFreqTable[_channel[ch].frequency];
- for(i = 0; i < sampleCnt; i += 1) {
- int offset;
- int16 data;
- offset = (_channel[ch].counter >> 12) & 0x1F;
- _channel[ch].counter += step;
- _channel[ch].counter &= 0x1FFFF;
- data = _channel[ch].waveform[offset];
- samples[2*i] += (int16)(vll * (data - 16));
- samples[2*i + 1] += (int16)(vlr * (data - 16));
- }
- }
- }
- }
+ for(ch = 0; ch < 6; ch++) {
+ // Only look at enabled channels
+ if(_channel[ch].control & 0x80) {
+ int lal = (_channel[ch].balance >> 4) & 0x0F;
+ int ral = (_channel[ch].balance >> 0) & 0x0F;
+ int al = _channel[ch].control & 0x1F;
+
+ lal = scale_tab[lal];
+ ral = scale_tab[ral];
+
+ // Calculate volume just as the patent says
+ vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
+ if(vll > 0x1F) vll = 0x1F;
+
+ vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
+ if(vlr > 0x1F) vlr = 0x1F;
+
+ vll = _volumeTable[vll];
+ vlr = _volumeTable[vlr];
+
+ // Check channel mode
+ if(_channel[ch].control & 0x40) {
+ /* DDA mode */
+ for(i = 0; i < sampleCnt; i++) {
+ samples[2*i] += (int16)(vll * (_channel[ch].dda - 16));
+ samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16));
+ }
+ } else {
+ /* Waveform mode */
+ uint32 step = _waveFreqTable[_channel[ch].frequency];
+ for(i = 0; i < sampleCnt; i += 1) {
+ int offset;
+ int16 data;
+ offset = (_channel[ch].counter >> 12) & 0x1F;
+ _channel[ch].counter += step;
+ _channel[ch].counter &= 0x1FFFF;
+ data = _channel[ch].waveform[offset];
+ samples[2*i] += (int16)(vll * (data - 16));
+ samples[2*i + 1] += (int16)(vlr * (data - 16));
+ }
+ }
+ }
+ }
}
@@ -466,7 +466,7 @@ void Player_PCE::procA541(channel_t *channel) {
channel->controlVec24 = false;
channel->controlVec21 = 0;
- channel->waveformCtrl = 0x80;
+ channel->waveformCtrl = 0x80;
}
// A592
diff --git a/engines/scumm/player_v2.cpp b/engines/scumm/player_v2.cpp
index c1c9b3930e..60103b20fb 100644
--- a/engines/scumm/player_v2.cpp
+++ b/engines/scumm/player_v2.cpp
@@ -23,8 +23,6 @@
*
*/
-
-#include "engines/engine.h"
#include "scumm/player_v2.h"
#include "scumm/scumm.h"
#include "sound/mididrv.h"
@@ -32,373 +30,51 @@
namespace Scumm {
-#define FREQ_HZ 236 // Don't change!
-
#define SPK_DECAY 0xa000 /* Depends on sample rate */
#define PCJR_DECAY 0xa000 /* Depends on sample rate */
-#define FIXP_SHIFT 16
-#define MAX_OUTPUT 0x7fff
-
#define NG_PRESET 0x0f35 /* noise generator preset */
#define FB_WNOISE 0x12000 /* feedback for white noise */
#define FB_PNOISE 0x08000 /* feedback for periodic noise */
-const uint8 note_lengths[] = {
- 0,
- 0, 0, 2,
- 0, 3, 4,
- 5, 6, 8,
- 9, 12, 16,
- 18, 24, 32,
- 36, 48, 64,
- 72, 96
-};
-
-static const uint16 hull_offsets[] = {
- 0, 12, 24, 36, 48, 60,
- 72, 88, 104, 120, 136, 256,
- 152, 164, 180
-};
-
-static const int16 hulls[] = {
- // hull 0
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, -1, 0, 0,
- // hull 1 (staccato)
- 3, -1, 0, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 2 (legato)
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0,
- // hull 3 (staccatissimo)
- 3, -1, 0, 2, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 4
- 3, -1, 0, 6, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 5
- 3, -1, 0, 16, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 6
- (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
- (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 7
- (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
- 28000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 8
- (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
- 28000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 9
- (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
- (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
- // hull 10
- (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
- (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 12
- 0, -1, 150, 340, -150, 340, 0, -1,
- 0, -1, 0, 0,
- // hull 13 == 164
- 20000, -1, 4000, 7, 1000, 15, 0, 0,
- (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
-
- // hull 14 == 180
- (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
- (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
-
- // hull misc = 196
- (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- // hull 11 == 256
- (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
- 0, -1, 0, 0
-};
-
-static const uint16 freqmod_lengths[] = {
- 0x1000, 0x1000, 0x20, 0x2000, 0x1000
-};
-
-static const uint16 freqmod_offsets[] = {
- 0, 0x100, 0x200, 0x302, 0x202
-};
-
-static const int8 freqmod_table[0x502] = {
- 0, 3, 6, 9, 12, 15, 18, 21,
- 24, 27, 30, 33, 36, 39, 42, 45,
- 48, 51, 54, 57, 59, 62, 65, 67,
- 70, 73, 75, 78, 80, 82, 85, 87,
- 89, 91, 94, 96, 98, 100, 102, 103,
- 105, 107, 108, 110, 112, 113, 114, 116,
- 117, 118, 119, 120, 121, 122, 123, 123,
- 124, 125, 125, 126, 126, 126, 126, 126,
- 126, 126, 126, 126, 126, 126, 125, 125,
- 124, 123, 123, 122, 121, 120, 119, 118,
- 117, 116, 114, 113, 112, 110, 108, 107,
- 105, 103, 102, 100, 98, 96, 94, 91,
- 89, 87, 85, 82, 80, 78, 75, 73,
- 70, 67, 65, 62, 59, 57, 54, 51,
- 48, 45, 42, 39, 36, 33, 30, 27,
- 24, 21, 18, 15, 12, 9, 6, 3,
- 0, -3, -6, -9, -12, -15, -18, -21,
- -24, -27, -30, -33, -36, -39, -42, -45,
- -48, -51, -54, -57, -59, -62, -65, -67,
- -70, -73, -75, -78, -80, -82, -85, -87,
- -89, -91, -94, -96, -98,-100,-102,-103,
- -105,-107,-108,-110,-112,-113,-114,-116,
- -117,-118,-119,-120,-121,-122,-123,-123,
- -124,-125,-125,-126,-126,-126,-126,-126,
- -126,-126,-126,-126,-126,-126,-125,-125,
- -124,-123,-123,-122,-121,-120,-119,-118,
- -117,-116,-114,-113,-112,-110,-108,-107,
- -105,-103,-102,-100, -98, -96, -94, -91,
- -89, -87, -85, -82, -80, -78, -75, -73,
- -70, -67, -65, -62, -59, -57, -54, -51,
- -48, -45, -42, -39, -36, -33, -30, -27,
- -24, -21, -18, -15, -12, -9, -6, -3,
-
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127,
- -128,-127,-126,-125,-124,-123,-122,-121,
- -120,-119,-118,-117,-116,-115,-114,-113,
- -112,-111,-110,-109,-108,-107,-106,-105,
- -104,-103,-102,-101,-100, -99, -98, -97,
- -96, -95, -94, -93, -92, -91, -90, -89,
- -88, -87, -86, -85, -84, -83, -82, -81,
- -80, -79, -78, -77, -76, -75, -74, -73,
- -72, -71, -70, -69, -68, -67, -66, -65,
- -64, -63, -62, -61, -60, -59, -58, -57,
- -56, -55, -54, -53, -52, -51, -50, -49,
- -48, -47, -46, -45, -44, -43, -42, -41,
- -40, -39, -38, -37, -36, -35, -34, -33,
- -32, -31, -30, -29, -28, -27, -26, -25,
- -24, -23, -22, -21, -20, -19, -18, -17,
- -16, -15, -14, -13, -12, -11, -10, -9,
- -8, -7, -6, -5, -4, -3, -2, -1,
-
- -120, 120,
-
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
-
- 41, 35, -66,-124, -31, 108, -42, -82,
- 82,-112, 73, -15, -15, -69, -23, -21,
- -77, -90, -37, 60,-121, 12, 62,-103,
- 36, 94, 13, 28, 6, -73, 71, -34,
- -77, 18, 77, -56, 67, -69,-117, -90,
- 31, 3, 90, 125, 9, 56, 37, 31,
- 93, -44, -53, -4,-106, -11, 69, 59,
- 19, 13,-119, 10, 28, -37, -82, 50,
- 32,-102, 80, -18, 64, 120, 54, -3,
- 18, 73, 50, -10, -98, 125, 73, -36,
- -83, 79, 20, -14, 68, 64, 102, -48,
- 107, -60, 48, -73, 50, 59, -95, 34,
- -10, 34,-111, -99, -31,-117, 31, -38,
- -80, -54,-103, 2, -71, 114, -99, 73,
- 44,-128, 126, -59,-103, -43, -23,-128,
- -78, -22, -55, -52, 83, -65, 103, -42,
- -65, 20, -42, 126, 45, -36,-114, 102,
- -125, -17, 87, 73, 97, -1, 105,-113,
- 97, -51, -47, 30, -99,-100, 22, 114,
- 114, -26, 29, -16,-124, 79, 74, 119,
- 2, -41, -24, 57, 44, 83, -53, -55,
- 18, 30, 51, 116, -98, 12, -12, -43,
- -44, -97, -44, -92, 89, 126, 53, -49,
- 50, 34, -12, -52, -49, -45,-112, 45,
- 72, -45,-113, 117, -26, -39, 29, 42,
- -27, -64, -9, 43, 120,-127,-121, 68,
- 14, 95, 80, 0, -44, 97,-115, -66,
- 123, 5, 21, 7, 59, 51,-126, 31,
- 24, 112,-110, -38, 100, 84, -50, -79,
- -123, 62, 105, 21, -8, 70, 106, 4,
- -106, 115, 14, -39, 22, 47, 103, 104,
- -44, -9, 74, 74, -48, 87, 104, 118,
- -6, 22, -69, 17, -83, -82, 36,-120,
- 121, -2, 82, -37, 37, 67, -27, 60,
- -12, 69, -45, -40, 40, -50, 11, -11,
- -59, 96, 89, 61,-105, 39,-118, 89,
- 118, 45, -48, -62, -55, -51, 104, -44,
- 73, 106, 121, 37, 8, 97, 64, 20,
- -79, 59, 106, -91, 17, 40, -63,-116,
- -42, -87, 11,-121,-105,-116, 47, -15,
- 21, 29,-102,-107, -63,-101, -31, -64,
- 126, -23, -88,-102, -89,-122, -62, -75,
- 84, -65,-102, -25, -39, 35, -47, 85,
- -112, 56, 40, -47, -39, 108, -95, 102,
- 94, 78, -31, 48,-100, -2, -39, 113,
- -97, -30, -91, -30, 12,-101, -76, 71,
- 101, 56, 42, 70,-119, -87,-126, 121,
- 122, 118, 120, -62, 99, -79, 38, -33,
- -38, 41, 109, 62, 98, -32,-106, 18,
- 52, -65, 57, -90, 63,-119, 94, -15,
- 109, 14, -29, 108, 40, -95, 30, 32,
- 29, -53, -62, 3, 63, 65, 7,-124,
- 15, 20, 5, 101, 27, 40, 97, -55,
- -59, -25, 44,-114, 70, 54, 8, -36,
- -13, -88,-115, -2, -66, -14, -21, 113,
- -1, -96, -48, 59, 117, 6,-116, 126,
- -121, 120, 115, 77, -48, -66,-126, -66,
- -37, -62, 70, 65, 43,-116, -6, 48,
- 127, 112, -16, -89, 84,-122, 50,-107,
- -86, 91, 104, 19, 11, -26, -4, -11,
- -54, -66, 125, -97,-119,-118, 65, 27,
- -3, -72, 79, 104, -10, 114, 123, 20,
- -103, -51, -45, 13, -16, 68, 58, -76,
- -90, 102, 83, 51, 11, -53, -95, 16
-};
-
-
-static const uint16 spk_freq_table[12] = {
- 36484, 34436, 32503, 30679, 28957, 27332,
- 25798, 24350, 22983, 21693, 20476, 19326
-};
-
-static const uint16 pcjr_freq_table[12] = {
- 65472, 61760, 58304, 55040, 52032, 49024,
- 46272, 43648, 41216, 38912, 36736, 34624
-};
-
-
-Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr) {
- int i;
-
- _isV3Game = (scumm->_game.version >= 3);
- _vm = scumm;
- _mixer = mixer;
- _sampleRate = _mixer->getOutputRate();
-
- _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6;
- // Initialize sound queue
- _current_nr = _next_nr = 0;
- _current_data = _next_data = 0;
+Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr)
+ : Player_V2Base(scumm, mixer, pcjr) {
- // Initialize channel code
- for (i = 0; i < 4; ++i)
- clear_channel(i);
-
- _next_tick = 0;
- _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ;
-
- // Initialize V3 music timer
- _music_timer_ctr = _music_timer = 0;
- _ticks_per_music_timer = 65535;
+ int i;
// Initialize square generator
_level = 0;
_RNG = NG_PRESET;
- set_pcjr(pcjr);
- setMusicVolume(255);
-
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-}
-
-Player_V2::~Player_V2() {
- Common::StackLock lock(_mutex);
- _mixer->stopHandle(_soundHandle);
-}
-
-void Player_V2::set_pcjr(bool pcjr) {
- Common::StackLock lock(_mutex);
-
_pcjr = pcjr;
if (_pcjr) {
_decay = PCJR_DECAY;
_update_step = (_sampleRate << FIXP_SHIFT) / (111860 * 2);
- _freqs_table = pcjr_freq_table;
} else {
_decay = SPK_DECAY;
_update_step = (_sampleRate << FIXP_SHIFT) / (1193000 * 2);
- _freqs_table = spk_freq_table;
}
- /* adapt _decay to sample rate. It must be squared when
- * sample rate doubles.
- */
- int i;
+ // Adapt _decay to sample rate. It must be squared when
+ // sample rate doubles.
for (i = 0; (_sampleRate << i) < 30000; i++)
_decay = _decay * _decay / 65536;
_timer_output = 0;
for (i = 0; i < 4; i++)
_timer_count[i] = 0;
+
+ setMusicVolume(255);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+Player_V2::~Player_V2() {
+ Common::StackLock lock(_mutex);
+ _mixer->stopHandle(_soundHandle);
}
void Player_V2::setMusicVolume (int vol) {
@@ -421,33 +97,6 @@ void Player_V2::setMusicVolume (int vol) {
_volumetable[15] = 0;
}
-void Player_V2::chainSound(int nr, byte *data) {
- int offset = _header_len + (_pcjr ? 10 : 2);
-
- _current_nr = nr;
- _current_data = data;
-
- for (int i = 0; i < 4; i++) {
- clear_channel(i);
-
- _channels[i].d.music_script_nr = nr;
- if (data) {
- _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
- if (_channels[i].d.next_cmd)
- _channels[i].d.time_left = 1;
- }
- }
- _music_timer = 0;
-}
-
-void Player_V2::chainNextSound() {
- if (_next_nr) {
- chainSound(_next_nr, _next_data);
- _next_nr = 0;
- _next_data = 0;
- }
-}
-
void Player_V2::stopAllSounds() {
Common::StackLock lock(_mutex);
@@ -476,11 +125,11 @@ void Player_V2::stopSound(int nr) {
}
void Player_V2::startSound(int nr) {
+ Common::StackLock lock(_mutex);
+
byte *data = _vm->getResourceAddress(rtSound, nr);
assert(data);
- Common::StackLock lock(_mutex);
-
int cprio = _current_data ? *(_current_data + _header_len) : 0;
int prio = *(data + _header_len);
int nprio = _next_data ? *(_next_data + _header_len) : 0;
@@ -519,272 +168,11 @@ int Player_V2::getSoundStatus(int nr) const {
return _current_nr == nr || _next_nr == nr;
}
-
-void Player_V2::clear_channel(int i) {
- ChannelInfo *channel = &_channels[i];
- memset(channel, 0, sizeof(ChannelInfo));
-}
-
-int Player_V2::getMusicTimer() {
- if (_isV3Game)
- return _music_timer;
- else
- return _channels[0].d.music_timer;
-}
-
-void Player_V2::execute_cmd(ChannelInfo *channel) {
- uint16 value;
- int16 offset;
- uint8 *script_ptr;
- ChannelInfo * current_channel;
- ChannelInfo * dest_channel;
-
- current_channel = channel;
-
- if (channel->d.next_cmd == 0)
- goto check_stopped;
- script_ptr = &_current_data[channel->d.next_cmd];
-
- for (;;) {
- uint8 opcode = *script_ptr++;
- if (opcode >= 0xf8) {
- switch (opcode) {
- case 0xf8: // set hull curve
- debug(7, "channels[%lu]: hull curve %2d",
- (long)(channel - _channels), *script_ptr);
- channel->d.hull_curve = hull_offsets[*script_ptr / 2];
- script_ptr++;
- break;
-
- case 0xf9: // set freqmod curve
- debug(7, "channels[%lu]: freqmod curve %2d",
- (long)(channel - _channels), *script_ptr);
- channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
- channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
- script_ptr++;
- break;
-
- case 0xfd: // clear other channel
- value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
- debug(7, "clear channel %d", value);
- script_ptr += 2;
- // In Indy3, when traveling to Venice a command is
- // issued to clear channel 4. So we introduce a 4th
- // channel, which is never used. All OOB accesses are
- // mapped to this channel.
- //
- // The original game had room for 8 channels, but only
- // channels 0-3 are read, changes to other channels
- // had no effect.
- if (value >= ARRAYSIZE (_channels))
- value = 4;
- channel = &_channels[value];
- // fall through
-
- case 0xfa: // clear current channel
- if (opcode == 0xfa)
- debug(7, "clear channel");
- channel->d.next_cmd = 0;
- channel->d.base_freq = 0;
- channel->d.freq_delta = 0;
- channel->d.freq = 0;
- channel->d.volume = 0;
- channel->d.volume_delta = 0;
- channel->d.inter_note_pause = 0;
- channel->d.transpose = 0;
- channel->d.hull_curve = 0;
- channel->d.hull_offset = 0;
- channel->d.hull_counter = 0;
- channel->d.freqmod_table = 0;
- channel->d.freqmod_offset = 0;
- channel->d.freqmod_incr = 0;
- channel->d.freqmod_multiplier = 0;
- channel->d.freqmod_modulo = 0;
- break;
-
- case 0xfb: // ret from subroutine
- debug(7, "ret from sub");
- script_ptr = _retaddr;
- break;
-
- case 0xfc: // call subroutine
- offset = READ_LE_UINT16 (script_ptr);
- debug(7, "subroutine %d", offset);
- script_ptr += 2;
- _retaddr = script_ptr;
- script_ptr = _current_data + offset;
- break;
-
- case 0xfe: // loop music
- opcode = *script_ptr++;
- offset = READ_LE_UINT16 (script_ptr);
- script_ptr += 2;
- debug(7, "loop if %d to %d", opcode, offset);
- if (!channel->array[opcode / 2] || --channel->array[opcode/2])
- script_ptr += offset;
- break;
-
- case 0xff: // set parameter
- opcode = *script_ptr++;
- value = READ_LE_UINT16 (script_ptr);
- channel->array[opcode / 2] = value;
- debug(7, "channels[%lu]: set param %2d = %5d",
- (long)(channel - _channels), opcode, value);
- script_ptr += 2;
- if (opcode == 14) {
- /* tempo var */
- _ticks_per_music_timer = 125;
- }
- if (opcode == 0)
- goto end;
- break;
- }
- } else { // opcode < 0xf8
- for (;;) {
- int16 note, octave;
- int is_last_note;
- dest_channel = &_channels[(opcode >> 5) & 3];
-
- if (!(opcode & 0x80)) {
-
- int tempo = channel->d.tempo;
- if (!tempo)
- tempo = 1;
- channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
-
- note = *script_ptr++;
- is_last_note = note & 0x80;
- note &= 0x7f;
- if (note == 0x7f) {
- debug(8, "channels[%lu]: pause %d",
- (long)(channel - _channels), channel->d.time_left);
- goto end;
- }
- } else {
-
- channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
-
- if ((opcode & 0x10)) {
- debug(8, "channels[%lu]: pause %d",
- (long)(channel - _channels), channel->d.time_left);
- goto end;
- }
-
- is_last_note = 0;
- note = (*script_ptr++) & 0x7f;
- }
-
- debug(8, "channels[%lu]: @%04lx note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
- (long)(dest_channel - channel), (long)(script_ptr ? script_ptr - _current_data - 2 : 0),
- note, (signed short) dest_channel->d.transpose, channel->d.time_left,
- dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
- dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
- is_last_note ? "last":"");
-
- uint16 myfreq;
- dest_channel->d.time_left = channel->d.time_left;
- dest_channel->d.note_length =
- channel->d.time_left - dest_channel->d.inter_note_pause;
- note += dest_channel->d.transpose;
- while (note < 0)
- note += 12;
- octave = note / 12;
- note = note % 12;
- dest_channel->d.hull_offset = 0;
- dest_channel->d.hull_counter = 1;
- if (_pcjr && dest_channel == &_channels[3]) {
- dest_channel->d.hull_curve = 196 + note * 12;
- myfreq = 384 - 64 * octave;
- } else {
- myfreq = _freqs_table[note] >> octave;
- }
- dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
- if (is_last_note)
- goto end;
- opcode = *script_ptr++;
- }
- }
- }
-
-end:
- channel = current_channel;
- if (channel->d.time_left) {
- channel->d.next_cmd = script_ptr - _current_data;
- return;
- }
-
- channel->d.next_cmd = 0;
-
-check_stopped:
- int i;
- for (i = 0; i < 4; i++) {
- if (_channels[i].d.time_left)
- return;
- }
-
- _current_nr = 0;
- _current_data = 0;
- chainNextSound();
- return;
-}
-
-void Player_V2::next_freqs(ChannelInfo *channel) {
- channel->d.volume += channel->d.volume_delta;
- channel->d.base_freq += channel->d.freq_delta;
-
- channel->d.freqmod_offset += channel->d.freqmod_incr;
- if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
- channel->d.freqmod_offset -= channel->d.freqmod_modulo;
- channel->d.freq =
- (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
- * (int) channel->d.freqmod_multiplier / 256
- + channel->d.base_freq;
-
- debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
- channel->d.base_freq, (int16)channel->d.freq_delta,
- channel->d.freqmod_table, channel->d.freqmod_offset,
- channel->d.freqmod_incr, channel->d.freqmod_multiplier,
- channel->d.freq);
-
- if (channel->d.note_length && !--channel->d.note_length) {
- channel->d.hull_offset = 16;
- channel->d.hull_counter = 1;
- }
-
- if (!--channel->d.time_left) {
- execute_cmd(channel);
- }
-
-#if 0
- debug(9, "channels[%d]: freq %d hull %d/%d/%d",
- channel - &_channels[0], channel->d.freq,
- channel->d.hull_curve, channel->d.hull_offset,
- channel->d.hull_counter);
-#endif
-
- if (channel->d.hull_counter && !--channel->d.hull_counter) {
- for (;;) {
- const int16 *hull_ptr = hulls
- + channel->d.hull_curve + channel->d.hull_offset / 2;
- if (hull_ptr[1] == -1) {
- channel->d.volume = hull_ptr[0];
- if (hull_ptr[0] == 0)
- channel->d.volume_delta = 0;
- channel->d.hull_offset += 4;
- } else {
- channel->d.volume_delta = hull_ptr[0];
- channel->d.hull_counter = hull_ptr[1];
- channel->d.hull_offset += 4;
- break;
- }
- }
- }
-}
-
-void Player_V2::do_mix(int16 *data, uint len) {
+int Player_V2::readBuffer(int16 *data, const int numSamples) {
Common::StackLock lock(_mutex);
uint step;
+ uint len = numSamples / 2;
do {
if (!(_next_tick >> FIXP_SHIFT)) {
@@ -802,18 +190,8 @@ void Player_V2::do_mix(int16 *data, uint len) {
data += 2 * step;
_next_tick -= step << FIXP_SHIFT;
} while (len -= step);
-}
-void Player_V2::nextTick() {
- for (int i = 0; i < 4; i++) {
- if (!_channels[i].d.time_left)
- continue;
- next_freqs(&_channels[i]);
- }
- if (_music_timer_ctr++ >= _ticks_per_music_timer) {
- _music_timer_ctr = 0;
- _music_timer++;
- }
+ return numSamples;
}
void Player_V2::lowPassFilter(int16 *sample, uint len) {
diff --git a/engines/scumm/player_v2.h b/engines/scumm/player_v2.h
index 2c3e784928..6a0b3d6d5e 100644
--- a/engines/scumm/player_v2.h
+++ b/engines/scumm/player_v2.h
@@ -26,84 +26,35 @@
#ifndef SCUMM_PLAYER_V2_H
#define SCUMM_PLAYER_V2_H
-#include "common/scummsys.h"
-#include "common/mutex.h"
-#include "scumm/music.h"
-#include "sound/audiostream.h"
-#include "sound/mixer.h"
+#include "scumm/player_v2base.h"
namespace Scumm {
-class ScummEngine;
-
-#include "common/pack-start.h" // START STRUCT PACKING
-
-struct channel_data {
- uint16 time_left; // 00
- uint16 next_cmd; // 02
- uint16 base_freq; // 04
- uint16 freq_delta; // 06
- uint16 freq; // 08
- uint16 volume; // 10
- uint16 volume_delta; // 12
- uint16 tempo; // 14
- uint16 inter_note_pause; // 16
- uint16 transpose; // 18
- uint16 note_length; // 20
- uint16 hull_curve; // 22
- uint16 hull_offset; // 24
- uint16 hull_counter; // 26
- uint16 freqmod_table; // 28
- uint16 freqmod_offset; // 30
- uint16 freqmod_incr; // 32
- uint16 freqmod_multiplier; // 34
- uint16 freqmod_modulo; // 36
- uint16 unknown[4]; // 38 - 44
- uint16 music_timer; // 46
- uint16 music_script_nr; // 48
-} PACKED_STRUCT;
-
-#include "common/pack-end.h" // END STRUCT PACKING
-
-
/**
* Scumm V2 PC-Speaker MIDI driver.
* This simulates the pc speaker sound, which is driven by the 8253 (square
* wave generator) and a low-band filter.
*/
-class Player_V2 : public Audio::AudioStream, public MusicEngine {
+class Player_V2 : public Player_V2Base {
public:
Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr);
virtual ~Player_V2();
+ // MusicEngine API
virtual void setMusicVolume(int vol);
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer();
+// virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
// AudioStream API
- int readBuffer(int16 *buffer, const int numSamples) {
- do_mix(buffer, numSamples / 2);
- return numSamples;
- }
+ int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return true; }
bool endOfData() const { return false; }
int getRate() const { return _sampleRate; }
protected:
- bool _isV3Game;
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
- ScummEngine *_vm;
-
- bool _pcjr;
- int _header_len;
-
- uint32 _sampleRate;
- uint32 _next_tick;
- uint32 _tick_len;
unsigned int _update_step;
unsigned int _decay;
int _level;
@@ -113,218 +64,13 @@ protected:
int _timer_count[4];
int _timer_output;
- int _current_nr;
- byte *_current_data;
- int _next_nr;
- byte *_next_data;
- byte *_retaddr;
-
- Common::Mutex _mutex;
-
-private:
- union ChannelInfo {
- channel_data d;
- uint16 array[sizeof(channel_data)/2];
- };
-
- int _music_timer;
- int _music_timer_ctr;
- int _ticks_per_music_timer;
-
- const uint16 *_freqs_table;
-
- ChannelInfo _channels[5];
-
protected:
- virtual void nextTick();
- virtual void clear_channel(int i);
- virtual void chainSound(int nr, byte *data);
- virtual void chainNextSound();
-
virtual void generateSpkSamples(int16 *data, uint len);
virtual void generatePCjrSamples(int16 *data, uint len);
void lowPassFilter(int16 *data, uint len);
void squareGenerator(int channel, int freq, int vol,
int noiseFeedback, int16 *sample, uint len);
-
-private:
- void do_mix(int16 *buf, uint len);
-
- void set_pcjr(bool pcjr);
- void execute_cmd(ChannelInfo *channel);
- void next_freqs(ChannelInfo *channel);
-};
-
-/**
- * Scumm V2 CMS/Gameblaster MIDI driver.
- */
-class Player_V2CMS : public Audio::AudioStream, public MusicEngine {
-public:
- Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer);
- virtual ~Player_V2CMS();
-
- virtual void setMusicVolume(int vol);
- virtual void startSound(int sound);
- virtual void stopSound(int sound);
- virtual void stopAllSounds();
- virtual int getMusicTimer();
- virtual int getSoundStatus(int sound) const;
-
- // AudioStream API
- int readBuffer(int16 *buffer, const int numSamples);
- bool isStereo() const { return true; }
- bool endOfData() const { return false; }
- int getRate() const { return _sampleRate; }
-
-protected:
-
-#include "common/pack-start.h" // START STRUCT PACKING
- struct Voice {
- byte attack;
- byte decay;
- byte sustain;
- byte release;
- byte octadd;
- int16 vibrato;
- int16 vibrato2;
- int16 noise;
- } PACKED_STRUCT;
-
- struct Voice2 {
- byte *amplitudeOutput;
- byte *freqOutput;
- byte *octaveOutput;
-
- uint8 channel;
- int8 sustainLevel;
- int8 attackRate;
- uint8 maxAmpl;
- int8 decayRate;
- int8 sustainRate;
- int8 releaseRate;
- int8 releaseTime;
- int8 vibratoRate;
- int8 vibratoDepth;
-
- int8 curVibratoRate;
- int8 curVibratoUnk;
-
- int8 unkVibratoRate;
- int8 unkVibratoDepth;
-
- int8 unkRate;
- int8 unkCount;
-
- int nextProcessState;
- int8 curVolume;
- int8 curOctave;
- int8 curFreq;
-
- int8 octaveAdd;
-
- int8 playingNote;
- Voice2 *nextVoice;
-
- byte chanNumber;
- } PACKED_STRUCT;
-
- struct MusicChip {
- byte ampl[4];
- byte freq[4];
- byte octave[2];
- } PACKED_STRUCT;
-#include "common/pack-end.h" // END STRUCT PACKING
-
- Voice _cmsVoicesBase[16];
- Voice2 _cmsVoices[8];
- MusicChip _cmsChips[2];
-
- int8 _tempo;
- int8 _tempoSum;
- byte _looping;
- byte _octaveMask;
- int16 _midiDelay;
- Voice2 *_midiChannel[16];
- byte _midiChannelUse[16];
- byte *_midiData;
- byte *_midiSongBegin;
-
- int _loadedMidiSong;
-
- byte _lastMidiCommand;
- uint _outputTableReady;
- byte _clkFrequenz;
- byte _restart;
- byte _curSno;
-
- void loadMidiData(byte *data, int sound);
- void play();
-
- void processChannel(Voice2 *channel);
- void processRelease(Voice2 *channel);
- void processAttack(Voice2 *channel);
- void processDecay(Voice2 *channel);
- void processSustain(Voice2 *channel);
- void processVibrato(Voice2 *channel);
-
- void playMusicChips(const MusicChip *table);
- void playNote(byte *&data);
- void clearNote(byte *&data);
- void offAllChannels();
- void playVoice();
- void processMidiData(uint ticks);
-
- Voice2 *getFreeVoice();
- Voice2 *getPlayVoice(byte param);
-
- // from Player_V2
-protected:
- bool _isV3Game;
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
- ScummEngine *_vm;
-
- int _header_len;
-
- uint32 _sampleRate;
- uint32 _next_tick;
- uint32 _tick_len;
-
- int _timer_count[4];
- int _timer_output;
-
- int _current_nr;
- byte *_current_data;
- int _next_nr;
- byte *_next_data;
- byte *_retaddr;
-
- Common::Mutex _mutex;
-
-private:
- union ChannelInfo {
- channel_data d;
- uint16 array[sizeof(channel_data)/2];
- };
-
- int _music_timer;
- int _music_timer_ctr;
- int _ticks_per_music_timer;
-
- ChannelInfo _channels[5];
-
-protected:
- virtual void nextTick();
- virtual void clear_channel(int i);
- virtual void chainSound(int nr, byte *data);
- virtual void chainNextSound();
-
-private:
- void do_mix(int16 *buf, uint len);
-
- void execute_cmd(ChannelInfo *channel);
- void next_freqs(ChannelInfo *channel);
};
} // End of namespace Scumm
diff --git a/engines/scumm/player_v2base.cpp b/engines/scumm/player_v2base.cpp
new file mode 100644
index 0000000000..61c91aae85
--- /dev/null
+++ b/engines/scumm/player_v2base.cpp
@@ -0,0 +1,657 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "scumm/player_v2base.h"
+#include "scumm/scumm.h"
+
+#define FREQ_HZ 236 // Don't change!
+
+#define MAX_OUTPUT 0x7fff
+
+namespace Scumm {
+
+const uint8 note_lengths[] = {
+ 0,
+ 0, 0, 2,
+ 0, 3, 4,
+ 5, 6, 8,
+ 9, 12, 16,
+ 18, 24, 32,
+ 36, 48, 64,
+ 72, 96
+};
+
+static const uint16 hull_offsets[] = {
+ 0, 12, 24, 36, 48, 60,
+ 72, 88, 104, 120, 136, 256,
+ 152, 164, 180
+};
+
+static const int16 hulls[] = {
+ // hull 0
+ 3, -1, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0,
+ // hull 1 (staccato)
+ 3, -1, 0, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 2 (legato)
+ 3, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // hull 3 (staccatissimo)
+ 3, -1, 0, 2, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 4
+ 3, -1, 0, 6, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 5
+ 3, -1, 0, 16, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 6
+ (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
+ (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
+ // hull 7
+ (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
+ 28000, -1, -5000, 5, 0, -1, 0, 0,
+ // hull 8
+ (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
+ 28000, -1, -6000, 5, 0, -1, 0, 0,
+ // hull 9
+ (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
+ (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
+ // hull 10
+ (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
+ (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
+ // hull 12
+ 0, -1, 150, 340, -150, 340, 0, -1,
+ 0, -1, 0, 0,
+ // hull 13 == 164
+ 20000, -1, 4000, 7, 1000, 15, 0, 0,
+ (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
+
+ // hull 14 == 180
+ (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
+ (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
+
+ // hull misc = 196
+ (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ // hull 11 == 256
+ (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0
+};
+
+static const uint16 freqmod_lengths[] = {
+ 0x1000, 0x1000, 0x20, 0x2000, 0x1000
+};
+
+static const uint16 freqmod_offsets[] = {
+ 0, 0x100, 0x200, 0x302, 0x202
+};
+
+static const int8 freqmod_table[0x502] = {
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 59, 62, 65, 67,
+ 70, 73, 75, 78, 80, 82, 85, 87,
+ 89, 91, 94, 96, 98, 100, 102, 103,
+ 105, 107, 108, 110, 112, 113, 114, 116,
+ 117, 118, 119, 120, 121, 122, 123, 123,
+ 124, 125, 125, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 126, 125, 125,
+ 124, 123, 123, 122, 121, 120, 119, 118,
+ 117, 116, 114, 113, 112, 110, 108, 107,
+ 105, 103, 102, 100, 98, 96, 94, 91,
+ 89, 87, 85, 82, 80, 78, 75, 73,
+ 70, 67, 65, 62, 59, 57, 54, 51,
+ 48, 45, 42, 39, 36, 33, 30, 27,
+ 24, 21, 18, 15, 12, 9, 6, 3,
+ 0, -3, -6, -9, -12, -15, -18, -21,
+ -24, -27, -30, -33, -36, -39, -42, -45,
+ -48, -51, -54, -57, -59, -62, -65, -67,
+ -70, -73, -75, -78, -80, -82, -85, -87,
+ -89, -91, -94, -96, -98,-100,-102,-103,
+ -105,-107,-108,-110,-112,-113,-114,-116,
+ -117,-118,-119,-120,-121,-122,-123,-123,
+ -124,-125,-125,-126,-126,-126,-126,-126,
+ -126,-126,-126,-126,-126,-126,-125,-125,
+ -124,-123,-123,-122,-121,-120,-119,-118,
+ -117,-116,-114,-113,-112,-110,-108,-107,
+ -105,-103,-102,-100, -98, -96, -94, -91,
+ -89, -87, -85, -82, -80, -78, -75, -73,
+ -70, -67, -65, -62, -59, -57, -54, -51,
+ -48, -45, -42, -39, -36, -33, -30, -27,
+ -24, -21, -18, -15, -12, -9, -6, -3,
+
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ -128,-127,-126,-125,-124,-123,-122,-121,
+ -120,-119,-118,-117,-116,-115,-114,-113,
+ -112,-111,-110,-109,-108,-107,-106,-105,
+ -104,-103,-102,-101,-100, -99, -98, -97,
+ -96, -95, -94, -93, -92, -91, -90, -89,
+ -88, -87, -86, -85, -84, -83, -82, -81,
+ -80, -79, -78, -77, -76, -75, -74, -73,
+ -72, -71, -70, -69, -68, -67, -66, -65,
+ -64, -63, -62, -61, -60, -59, -58, -57,
+ -56, -55, -54, -53, -52, -51, -50, -49,
+ -48, -47, -46, -45, -44, -43, -42, -41,
+ -40, -39, -38, -37, -36, -35, -34, -33,
+ -32, -31, -30, -29, -28, -27, -26, -25,
+ -24, -23, -22, -21, -20, -19, -18, -17,
+ -16, -15, -14, -13, -12, -11, -10, -9,
+ -8, -7, -6, -5, -4, -3, -2, -1,
+
+ -120, 120,
+
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+
+ 41, 35, -66,-124, -31, 108, -42, -82,
+ 82,-112, 73, -15, -15, -69, -23, -21,
+ -77, -90, -37, 60,-121, 12, 62,-103,
+ 36, 94, 13, 28, 6, -73, 71, -34,
+ -77, 18, 77, -56, 67, -69,-117, -90,
+ 31, 3, 90, 125, 9, 56, 37, 31,
+ 93, -44, -53, -4,-106, -11, 69, 59,
+ 19, 13,-119, 10, 28, -37, -82, 50,
+ 32,-102, 80, -18, 64, 120, 54, -3,
+ 18, 73, 50, -10, -98, 125, 73, -36,
+ -83, 79, 20, -14, 68, 64, 102, -48,
+ 107, -60, 48, -73, 50, 59, -95, 34,
+ -10, 34,-111, -99, -31,-117, 31, -38,
+ -80, -54,-103, 2, -71, 114, -99, 73,
+ 44,-128, 126, -59,-103, -43, -23,-128,
+ -78, -22, -55, -52, 83, -65, 103, -42,
+ -65, 20, -42, 126, 45, -36,-114, 102,
+ -125, -17, 87, 73, 97, -1, 105,-113,
+ 97, -51, -47, 30, -99,-100, 22, 114,
+ 114, -26, 29, -16,-124, 79, 74, 119,
+ 2, -41, -24, 57, 44, 83, -53, -55,
+ 18, 30, 51, 116, -98, 12, -12, -43,
+ -44, -97, -44, -92, 89, 126, 53, -49,
+ 50, 34, -12, -52, -49, -45,-112, 45,
+ 72, -45,-113, 117, -26, -39, 29, 42,
+ -27, -64, -9, 43, 120,-127,-121, 68,
+ 14, 95, 80, 0, -44, 97,-115, -66,
+ 123, 5, 21, 7, 59, 51,-126, 31,
+ 24, 112,-110, -38, 100, 84, -50, -79,
+ -123, 62, 105, 21, -8, 70, 106, 4,
+ -106, 115, 14, -39, 22, 47, 103, 104,
+ -44, -9, 74, 74, -48, 87, 104, 118,
+ -6, 22, -69, 17, -83, -82, 36,-120,
+ 121, -2, 82, -37, 37, 67, -27, 60,
+ -12, 69, -45, -40, 40, -50, 11, -11,
+ -59, 96, 89, 61,-105, 39,-118, 89,
+ 118, 45, -48, -62, -55, -51, 104, -44,
+ 73, 106, 121, 37, 8, 97, 64, 20,
+ -79, 59, 106, -91, 17, 40, -63,-116,
+ -42, -87, 11,-121,-105,-116, 47, -15,
+ 21, 29,-102,-107, -63,-101, -31, -64,
+ 126, -23, -88,-102, -89,-122, -62, -75,
+ 84, -65,-102, -25, -39, 35, -47, 85,
+ -112, 56, 40, -47, -39, 108, -95, 102,
+ 94, 78, -31, 48,-100, -2, -39, 113,
+ -97, -30, -91, -30, 12,-101, -76, 71,
+ 101, 56, 42, 70,-119, -87,-126, 121,
+ 122, 118, 120, -62, 99, -79, 38, -33,
+ -38, 41, 109, 62, 98, -32,-106, 18,
+ 52, -65, 57, -90, 63,-119, 94, -15,
+ 109, 14, -29, 108, 40, -95, 30, 32,
+ 29, -53, -62, 3, 63, 65, 7,-124,
+ 15, 20, 5, 101, 27, 40, 97, -55,
+ -59, -25, 44,-114, 70, 54, 8, -36,
+ -13, -88,-115, -2, -66, -14, -21, 113,
+ -1, -96, -48, 59, 117, 6,-116, 126,
+ -121, 120, 115, 77, -48, -66,-126, -66,
+ -37, -62, 70, 65, 43,-116, -6, 48,
+ 127, 112, -16, -89, 84,-122, 50,-107,
+ -86, 91, 104, 19, 11, -26, -4, -11,
+ -54, -66, 125, -97,-119,-118, 65, 27,
+ -3, -72, 79, 104, -10, 114, 123, 20,
+ -103, -51, -45, 13, -16, 68, 58, -76,
+ -90, 102, 83, 51, 11, -53, -95, 16
+};
+
+static const uint16 spk_freq_table[12] = {
+ 36484, 34436, 32503, 30679, 28957, 27332,
+ 25798, 24350, 22983, 21693, 20476, 19326
+};
+
+static const uint16 pcjr_freq_table[12] = {
+ 65472, 61760, 58304, 55040, 52032, 49024,
+ 46272, 43648, 41216, 38912, 36736, 34624
+};
+
+
+Player_V2Base::Player_V2Base(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr)
+ : _vm(scumm),
+ _mixer(mixer),
+ _pcjr(pcjr),
+ _sampleRate(_mixer->getOutputRate()) {
+
+ _isV3Game = (scumm->_game.version >= 3);
+
+ _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6;
+
+ // Initialize sound queue
+ _current_nr = _next_nr = 0;
+ _current_data = _next_data = 0;
+
+ // Initialize channel code
+ for (int i = 0; i < 4; ++i)
+ clear_channel(i);
+
+ _next_tick = 0;
+ _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ;
+
+ // Initialize V3 music timer
+ _music_timer_ctr = _music_timer = 0;
+ _ticks_per_music_timer = 65535;
+
+ if (_pcjr) {
+ _freqs_table = pcjr_freq_table;
+ } else {
+ _freqs_table = spk_freq_table;
+ }
+}
+
+Player_V2Base::~Player_V2Base() {
+}
+
+void Player_V2Base::chainSound(int nr, byte *data) {
+ int offset = _header_len + (_pcjr ? 10 : 2);
+
+ _current_nr = nr;
+ _current_data = data;
+
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+
+ _channels[i].d.music_script_nr = nr;
+ if (data) {
+ _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
+ if (_channels[i].d.next_cmd) {
+ _channels[i].d.time_left = 1;
+ }
+ }
+ }
+ _music_timer = 0;
+}
+
+void Player_V2Base::chainNextSound() {
+ if (_next_nr) {
+ chainSound(_next_nr, _next_data);
+ _next_nr = 0;
+ _next_data = 0;
+ }
+}
+
+// TODO: Merge stopAllSounds, stopSound() and startSound(), using some overriding
+// perhaps? Player_V2CMS's implementations start like that of Player_V2
+// but then add some MIDI related stuff.
+
+void Player_V2Base::clear_channel(int i) {
+ ChannelInfo *channel = &_channels[i];
+ memset(channel, 0, sizeof(ChannelInfo));
+}
+
+int Player_V2Base::getMusicTimer() {
+ if (_isV3Game)
+ return _music_timer;
+ else
+ return _channels[0].d.music_timer;
+}
+
+void Player_V2Base::execute_cmd(ChannelInfo *channel) {
+ uint16 value;
+ int16 offset;
+ uint8 *script_ptr;
+ ChannelInfo * current_channel;
+ ChannelInfo * dest_channel;
+
+ current_channel = channel;
+
+ if (channel->d.next_cmd == 0)
+ goto check_stopped;
+ script_ptr = &_current_data[channel->d.next_cmd];
+
+ for (;;) {
+ uint8 opcode = *script_ptr++;
+ if (opcode >= 0xf8) {
+ switch (opcode) {
+ case 0xf8: // set hull curve
+ debug(7, "channels[%d]: hull curve %2d",
+ (uint)(channel - _channels), *script_ptr);
+ channel->d.hull_curve = hull_offsets[*script_ptr / 2];
+ script_ptr++;
+ break;
+
+ case 0xf9: // set freqmod curve
+ debug(7, "channels[%d]: freqmod curve %2d",
+ (uint)(channel - _channels), *script_ptr);
+ channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
+ channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
+ script_ptr++;
+ break;
+
+ case 0xfd: // clear other channel
+ value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
+ debug(7, "clear channel %d", value);
+ script_ptr += 2;
+ // In Indy3, when traveling to Venice a command is
+ // issued to clear channel 4. So we introduce a 4th
+ // channel, which is never used. All OOB accesses are
+ // mapped to this channel.
+ //
+ // The original game had room for 8 channels, but only
+ // channels 0-3 are read, changes to other channels
+ // had no effect.
+ if (value >= ARRAYSIZE (_channels))
+ value = 4;
+ channel = &_channels[value];
+ // fall through
+
+ case 0xfa: // clear current channel
+ if (opcode == 0xfa)
+ debug(7, "clear channel");
+ channel->d.next_cmd = 0;
+ channel->d.base_freq = 0;
+ channel->d.freq_delta = 0;
+ channel->d.freq = 0;
+ channel->d.volume = 0;
+ channel->d.volume_delta = 0;
+ channel->d.inter_note_pause = 0;
+ channel->d.transpose = 0;
+ channel->d.hull_curve = 0;
+ channel->d.hull_offset = 0;
+ channel->d.hull_counter = 0;
+ channel->d.freqmod_table = 0;
+ channel->d.freqmod_offset = 0;
+ channel->d.freqmod_incr = 0;
+ channel->d.freqmod_multiplier = 0;
+ channel->d.freqmod_modulo = 0;
+ break;
+
+ case 0xfb: // ret from subroutine
+ debug(7, "ret from sub");
+ script_ptr = _retaddr;
+ break;
+
+ case 0xfc: // call subroutine
+ offset = READ_LE_UINT16 (script_ptr);
+ debug(7, "subroutine %d", offset);
+ script_ptr += 2;
+ _retaddr = script_ptr;
+ script_ptr = _current_data + offset;
+ break;
+
+ case 0xfe: // loop music
+ opcode = *script_ptr++;
+ offset = READ_LE_UINT16 (script_ptr);
+ script_ptr += 2;
+ debug(7, "loop if %d to %d", opcode, offset);
+ if (!channel->array[opcode / 2] || --channel->array[opcode/2])
+ script_ptr += offset;
+ break;
+
+ case 0xff: // set parameter
+ opcode = *script_ptr++;
+ value = READ_LE_UINT16 (script_ptr);
+ channel->array[opcode / 2] = value;
+ debug(7, "channels[%d]: set param %2d = %5d",
+ (uint)(channel - _channels), opcode, value);
+ script_ptr += 2;
+ if (opcode == 14) {
+ /* tempo var */
+ _ticks_per_music_timer = 125;
+ }
+ if (opcode == 0)
+ goto end;
+ break;
+ }
+ } else { // opcode < 0xf8
+ for (;;) {
+ int16 note, octave;
+ int is_last_note;
+ dest_channel = &_channels[(opcode >> 5) & 3];
+
+ if (!(opcode & 0x80)) {
+
+ int tempo = channel->d.tempo;
+ if (!tempo)
+ tempo = 1;
+ channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
+
+ note = *script_ptr++;
+ is_last_note = note & 0x80;
+ note &= 0x7f;
+ if (note == 0x7f) {
+ debug(8, "channels[%d]: pause %d",
+ (uint)(channel - _channels), channel->d.time_left);
+ goto end;
+ }
+ } else {
+
+ channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
+
+ if ((opcode & 0x10)) {
+ debug(8, "channels[%d]: pause %d",
+ (uint)(channel - _channels), channel->d.time_left);
+ goto end;
+ }
+
+ is_last_note = 0;
+ note = (*script_ptr++) & 0x7f;
+ }
+
+ debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
+ (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0,
+ note, (signed short) dest_channel->d.transpose, channel->d.time_left,
+ dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
+ dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
+ is_last_note ? "last":"");
+
+ uint16 myfreq;
+ dest_channel->d.time_left = channel->d.time_left;
+ dest_channel->d.note_length =
+ channel->d.time_left - dest_channel->d.inter_note_pause;
+ note += dest_channel->d.transpose;
+ while (note < 0)
+ note += 12;
+ octave = note / 12;
+ note = note % 12;
+ dest_channel->d.hull_offset = 0;
+ dest_channel->d.hull_counter = 1;
+ if (_pcjr && dest_channel == &_channels[3]) {
+ dest_channel->d.hull_curve = 196 + note * 12;
+ myfreq = 384 - 64 * octave;
+ } else {
+ myfreq = _freqs_table[note] >> octave;
+ }
+ dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
+ if (is_last_note)
+ goto end;
+ opcode = *script_ptr++;
+ }
+ }
+ }
+
+end:
+ channel = current_channel;
+ if (channel->d.time_left) {
+ channel->d.next_cmd = script_ptr - _current_data;
+ return;
+ }
+
+ channel->d.next_cmd = 0;
+
+check_stopped:
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (_channels[i].d.time_left)
+ return;
+ }
+
+ _current_nr = 0;
+ _current_data = 0;
+ chainNextSound();
+}
+
+void Player_V2Base::next_freqs(ChannelInfo *channel) {
+ channel->d.volume += channel->d.volume_delta;
+ channel->d.base_freq += channel->d.freq_delta;
+
+ channel->d.freqmod_offset += channel->d.freqmod_incr;
+ if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
+ channel->d.freqmod_offset -= channel->d.freqmod_modulo;
+
+ channel->d.freq =
+ (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
+ * (int) channel->d.freqmod_multiplier / 256
+ + channel->d.base_freq;
+
+ debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
+ channel->d.base_freq, (int16)channel->d.freq_delta,
+ channel->d.freqmod_table, channel->d.freqmod_offset,
+ channel->d.freqmod_incr, channel->d.freqmod_multiplier,
+ channel->d.freq);
+
+ if (channel->d.note_length && !--channel->d.note_length) {
+ channel->d.hull_offset = 16;
+ channel->d.hull_counter = 1;
+ }
+
+ if (!--channel->d.time_left) {
+ execute_cmd(channel);
+ }
+
+ if (channel->d.hull_counter && !--channel->d.hull_counter) {
+ for (;;) {
+ const int16 *hull_ptr = hulls
+ + channel->d.hull_curve + channel->d.hull_offset / 2;
+ if (hull_ptr[1] == -1) {
+ channel->d.volume = hull_ptr[0];
+ if (hull_ptr[0] == 0)
+ channel->d.volume_delta = 0;
+ channel->d.hull_offset += 4;
+ } else {
+ channel->d.volume_delta = hull_ptr[0];
+ channel->d.hull_counter = hull_ptr[1];
+ channel->d.hull_offset += 4;
+ break;
+ }
+ }
+ }
+}
+
+void Player_V2Base::nextTick() {
+ for (int i = 0; i < 4; i++) {
+ if (!_channels[i].d.time_left)
+ continue;
+ next_freqs(&_channels[i]);
+ }
+ if (_music_timer_ctr++ >= _ticks_per_music_timer) {
+ _music_timer_ctr = 0;
+ _music_timer++;
+ }
+}
+
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_v2base.h b/engines/scumm/player_v2base.h
new file mode 100644
index 0000000000..7b90ae98cf
--- /dev/null
+++ b/engines/scumm/player_v2base.h
@@ -0,0 +1,148 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_PLAYER_V2BASE_H
+#define SCUMM_PLAYER_V2BASE_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "scumm/music.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+class ScummEngine;
+
+
+#include "common/pack-start.h" // START STRUCT PACKING
+
+struct channel_data {
+ uint16 time_left; // 00
+ uint16 next_cmd; // 02
+ uint16 base_freq; // 04
+ uint16 freq_delta; // 06
+ uint16 freq; // 08
+ uint16 volume; // 10
+ uint16 volume_delta; // 12
+ uint16 tempo; // 14
+ uint16 inter_note_pause; // 16
+ uint16 transpose; // 18
+ uint16 note_length; // 20
+ uint16 hull_curve; // 22
+ uint16 hull_offset; // 24
+ uint16 hull_counter; // 26
+ uint16 freqmod_table; // 28
+ uint16 freqmod_offset; // 30
+ uint16 freqmod_incr; // 32
+ uint16 freqmod_multiplier; // 34
+ uint16 freqmod_modulo; // 36
+ uint16 unknown[4]; // 38 - 44
+ uint16 music_timer; // 46
+ uint16 music_script_nr; // 48
+} PACKED_STRUCT;
+
+#include "common/pack-end.h" // END STRUCT PACKING
+
+/**
+ * Common base class for Player_V2 and Player_V2CMS.
+ */
+class Player_V2Base : public Audio::AudioStream, public MusicEngine {
+public:
+ Player_V2Base(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr);
+ virtual ~Player_V2Base();
+
+ // MusicEngine API
+// virtual void setMusicVolume(int vol);
+// virtual void startSound(int sound);
+// virtual void stopSound(int sound);
+// virtual void stopAllSounds();
+ virtual int getMusicTimer();
+// virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+/*
+ int readBuffer(int16 *buffer, const int numSamples) {
+ do_mix(buffer, numSamples / 2);
+ return numSamples;
+ }
+*/
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sampleRate; }
+
+protected:
+ enum {
+ FIXP_SHIFT = 16
+ };
+
+ bool _isV3Game;
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _soundHandle;
+ ScummEngine *_vm;
+
+ bool _pcjr;
+ int _header_len;
+
+ const uint32 _sampleRate;
+ uint32 _next_tick;
+ uint32 _tick_len;
+
+ int _current_nr;
+ byte *_current_data;
+ int _next_nr;
+ byte *_next_data;
+ byte *_retaddr;
+
+ Common::Mutex _mutex;
+
+ union ChannelInfo {
+ channel_data d;
+ uint16 array[sizeof(channel_data)/2];
+ };
+
+ ChannelInfo _channels[5];
+
+private:
+ int _music_timer;
+ int _music_timer_ctr;
+ int _ticks_per_music_timer;
+
+ const uint16 *_freqs_table;
+
+protected:
+ virtual void nextTick();
+ virtual void clear_channel(int i);
+ virtual void chainSound(int nr, byte *data);
+ virtual void chainNextSound();
+
+ void execute_cmd(ChannelInfo *channel);
+ void next_freqs(ChannelInfo *channel);
+};
+
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp
index ba60a31061..7bec171173 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/player_v2cms.cpp
@@ -23,8 +23,7 @@
*
*/
-#include "engines/engine.h"
-#include "scumm/player_v2.h"
+#include "scumm/player_v2cms.h"
#include "scumm/scumm.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
@@ -32,15 +31,6 @@
namespace Scumm {
-#define FREQ_HZ 236 // Don't change!
-
-#define FIXP_SHIFT 16
-#define MAX_OUTPUT 0x7fff
-
-#define NG_PRESET 0x0f35 /* noise generator preset */
-#define FB_WNOISE 0x12000 /* feedback for white noise */
-#define FB_PNOISE 0x08000 /* feedback for periodic noise */
-
#define PROCESS_ATTACK 1
#define PROCESS_RELEASE 2
#define PROCESS_SUSTAIN 3
@@ -49,283 +39,6 @@ namespace Scumm {
#define CMS_RATE 22050
-const uint8 note_lengths[] = {
- 0,
- 0, 0, 2,
- 0, 3, 4,
- 5, 6, 8,
- 9, 12, 16,
- 18, 24, 32,
- 36, 48, 64,
- 72, 96
-};
-
-static const uint16 hull_offsets[] = {
- 0, 12, 24, 36, 48, 60,
- 72, 88, 104, 120, 136, 256,
- 152, 164, 180
-};
-
-static const int16 hulls[] = {
- // hull 0
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, -1, 0, 0,
- // hull 1 (staccato)
- 3, -1, 0, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 2 (legato)
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0,
- // hull 3 (staccatissimo)
- 3, -1, 0, 2, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 4
- 3, -1, 0, 6, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 5
- 3, -1, 0, 16, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 6
- (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
- (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 7
- (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
- 28000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 8
- (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
- 28000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 9
- (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
- (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
- // hull 10
- (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
- (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 12
- 0, -1, 150, 340, -150, 340, 0, -1,
- 0, -1, 0, 0,
- // hull 13 == 164
- 20000, -1, 4000, 7, 1000, 15, 0, 0,
- (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
-
- // hull 14 == 180
- (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
- (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
-
- // hull misc = 196
- (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- // hull 11 == 256
- (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
- 0, -1, 0, 0
-};
-
-static const uint16 freqmod_lengths[] = {
- 0x1000, 0x1000, 0x20, 0x2000, 0x1000
-};
-
-static const uint16 freqmod_offsets[] = {
- 0, 0x100, 0x200, 0x302, 0x202
-};
-
-static const int8 freqmod_table[0x502] = {
- 0, 3, 6, 9, 12, 15, 18, 21,
- 24, 27, 30, 33, 36, 39, 42, 45,
- 48, 51, 54, 57, 59, 62, 65, 67,
- 70, 73, 75, 78, 80, 82, 85, 87,
- 89, 91, 94, 96, 98, 100, 102, 103,
- 105, 107, 108, 110, 112, 113, 114, 116,
- 117, 118, 119, 120, 121, 122, 123, 123,
- 124, 125, 125, 126, 126, 126, 126, 126,
- 126, 126, 126, 126, 126, 126, 125, 125,
- 124, 123, 123, 122, 121, 120, 119, 118,
- 117, 116, 114, 113, 112, 110, 108, 107,
- 105, 103, 102, 100, 98, 96, 94, 91,
- 89, 87, 85, 82, 80, 78, 75, 73,
- 70, 67, 65, 62, 59, 57, 54, 51,
- 48, 45, 42, 39, 36, 33, 30, 27,
- 24, 21, 18, 15, 12, 9, 6, 3,
- 0, -3, -6, -9, -12, -15, -18, -21,
- -24, -27, -30, -33, -36, -39, -42, -45,
- -48, -51, -54, -57, -59, -62, -65, -67,
- -70, -73, -75, -78, -80, -82, -85, -87,
- -89, -91, -94, -96, -98,-100,-102,-103,
- -105,-107,-108,-110,-112,-113,-114,-116,
- -117,-118,-119,-120,-121,-122,-123,-123,
- -124,-125,-125,-126,-126,-126,-126,-126,
- -126,-126,-126,-126,-126,-126,-125,-125,
- -124,-123,-123,-122,-121,-120,-119,-118,
- -117,-116,-114,-113,-112,-110,-108,-107,
- -105,-103,-102,-100, -98, -96, -94, -91,
- -89, -87, -85, -82, -80, -78, -75, -73,
- -70, -67, -65, -62, -59, -57, -54, -51,
- -48, -45, -42, -39, -36, -33, -30, -27,
- -24, -21, -18, -15, -12, -9, -6, -3,
-
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127,
- -128,-127,-126,-125,-124,-123,-122,-121,
- -120,-119,-118,-117,-116,-115,-114,-113,
- -112,-111,-110,-109,-108,-107,-106,-105,
- -104,-103,-102,-101,-100, -99, -98, -97,
- -96, -95, -94, -93, -92, -91, -90, -89,
- -88, -87, -86, -85, -84, -83, -82, -81,
- -80, -79, -78, -77, -76, -75, -74, -73,
- -72, -71, -70, -69, -68, -67, -66, -65,
- -64, -63, -62, -61, -60, -59, -58, -57,
- -56, -55, -54, -53, -52, -51, -50, -49,
- -48, -47, -46, -45, -44, -43, -42, -41,
- -40, -39, -38, -37, -36, -35, -34, -33,
- -32, -31, -30, -29, -28, -27, -26, -25,
- -24, -23, -22, -21, -20, -19, -18, -17,
- -16, -15, -14, -13, -12, -11, -10, -9,
- -8, -7, -6, -5, -4, -3, -2, -1,
-
- -120, 120,
-
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
-
- 41, 35, -66,-124, -31, 108, -42, -82,
- 82,-112, 73, -15, -15, -69, -23, -21,
- -77, -90, -37, 60,-121, 12, 62,-103,
- 36, 94, 13, 28, 6, -73, 71, -34,
- -77, 18, 77, -56, 67, -69,-117, -90,
- 31, 3, 90, 125, 9, 56, 37, 31,
- 93, -44, -53, -4,-106, -11, 69, 59,
- 19, 13,-119, 10, 28, -37, -82, 50,
- 32,-102, 80, -18, 64, 120, 54, -3,
- 18, 73, 50, -10, -98, 125, 73, -36,
- -83, 79, 20, -14, 68, 64, 102, -48,
- 107, -60, 48, -73, 50, 59, -95, 34,
- -10, 34,-111, -99, -31,-117, 31, -38,
- -80, -54,-103, 2, -71, 114, -99, 73,
- 44,-128, 126, -59,-103, -43, -23,-128,
- -78, -22, -55, -52, 83, -65, 103, -42,
- -65, 20, -42, 126, 45, -36,-114, 102,
- -125, -17, 87, 73, 97, -1, 105,-113,
- 97, -51, -47, 30, -99,-100, 22, 114,
- 114, -26, 29, -16,-124, 79, 74, 119,
- 2, -41, -24, 57, 44, 83, -53, -55,
- 18, 30, 51, 116, -98, 12, -12, -43,
- -44, -97, -44, -92, 89, 126, 53, -49,
- 50, 34, -12, -52, -49, -45,-112, 45,
- 72, -45,-113, 117, -26, -39, 29, 42,
- -27, -64, -9, 43, 120,-127,-121, 68,
- 14, 95, 80, 0, -44, 97,-115, -66,
- 123, 5, 21, 7, 59, 51,-126, 31,
- 24, 112,-110, -38, 100, 84, -50, -79,
- -123, 62, 105, 21, -8, 70, 106, 4,
- -106, 115, 14, -39, 22, 47, 103, 104,
- -44, -9, 74, 74, -48, 87, 104, 118,
- -6, 22, -69, 17, -83, -82, 36,-120,
- 121, -2, 82, -37, 37, 67, -27, 60,
- -12, 69, -45, -40, 40, -50, 11, -11,
- -59, 96, 89, 61,-105, 39,-118, 89,
- 118, 45, -48, -62, -55, -51, 104, -44,
- 73, 106, 121, 37, 8, 97, 64, 20,
- -79, 59, 106, -91, 17, 40, -63,-116,
- -42, -87, 11,-121,-105,-116, 47, -15,
- 21, 29,-102,-107, -63,-101, -31, -64,
- 126, -23, -88,-102, -89,-122, -62, -75,
- 84, -65,-102, -25, -39, 35, -47, 85,
- -112, 56, 40, -47, -39, 108, -95, 102,
- 94, 78, -31, 48,-100, -2, -39, 113,
- -97, -30, -91, -30, 12,-101, -76, 71,
- 101, 56, 42, 70,-119, -87,-126, 121,
- 122, 118, 120, -62, 99, -79, 38, -33,
- -38, 41, 109, 62, 98, -32,-106, 18,
- 52, -65, 57, -90, 63,-119, 94, -15,
- 109, 14, -29, 108, 40, -95, 30, 32,
- 29, -53, -62, 3, 63, 65, 7,-124,
- 15, 20, 5, 101, 27, 40, 97, -55,
- -59, -25, 44,-114, 70, 54, 8, -36,
- -13, -88,-115, -2, -66, -14, -21, 113,
- -1, -96, -48, 59, 117, 6,-116, 126,
- -121, 120, 115, 77, -48, -66,-126, -66,
- -37, -62, 70, 65, 43,-116, -6, 48,
- 127, 112, -16, -89, 84,-122, 50,-107,
- -86, 91, 104, 19, 11, -26, -4, -11,
- -54, -66, 125, -97,-119,-118, 65, 27,
- -3, -72, 79, 104, -10, 114, 123, 20,
- -103, -51, -45, 13, -16, 68, 58, -76,
- -90, 102, 83, 51, 11, -53, -95, 16
-};
-
static const byte freqTable[] = {
3, 10, 17, 24, 31, 38, 45, 51,
58, 65, 71, 77, 83, 90, 96, 102,
@@ -416,50 +129,17 @@ static const byte releaseRate[] = {
36, 56, 80, 100, 120, 140, 160, 255
};
-static const uint16 pcjr_freq_table[12] = {
- 65472, 61760, 58304, 55040, 52032, 49024,
- 46272, 43648, 41216, 38912, 36736, 34624
-};
-
static const byte volumeTable[] = {
0x00, 0x10, 0x10, 0x11, 0x11, 0x21, 0x22, 0x22,
0x33, 0x44, 0x55, 0x66, 0x88, 0xAA, 0xCC, 0xFF
};
-static CMSEmulator *g_cmsEmu = 0;
-
-Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) {
+Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer)
+ : Player_V2Base(scumm, mixer, true) {
int i;
- _isV3Game = (scumm->_game.version >= 3);
- _vm = scumm;
- _mixer = mixer;
-// debug("mixer rate: %d", _mixer->getOutputRate());
- _sampleRate = CMS_RATE;
-
- _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6;
-
- // Initialize sound queue
- _current_nr = _next_nr = 0;
- _current_data = _next_data = 0;
-
- // Initialize channel code
- for (i = 0; i < 4; ++i)
- clear_channel(i);
-
- _next_tick = 0;
- _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ;
-
- // Initialize V3 music timer
- _music_timer_ctr = _music_timer = 0;
- _ticks_per_music_timer = 65535;
-
setMusicVolume(255);
- _timer_output = 0;
- for (i = 0; i < 4; i++)
- _timer_count[i] = 0;
-
memset(_cmsVoicesBase, 0, sizeof(Voice)*16);
memset(_cmsVoices, 0, sizeof(Voice2)*8);
memset(_cmsChips, 0, sizeof(MusicChip)*2);
@@ -495,7 +175,7 @@ Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) {
_cmsVoices[7].octaveOutput = &(_cmsChips[1].octave[1]);
// inits the CMS Emulator like in the original
- g_cmsEmu = new CMSEmulator(_sampleRate);
+ _cmsEmu = new CMSEmulator(_sampleRate);
static const byte cmsInitData[13*2] = {
0x1C, 0x02,
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00,
@@ -505,8 +185,8 @@ Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) {
i = 0;
for (int cmsPort = 0x220; i < 2; cmsPort += 2, ++i) {
for (int off = 0; off < 13; ++off) {
- g_cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]);
- g_cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]);
+ _cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]);
+ _cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]);
}
}
@@ -517,40 +197,12 @@ Player_V2CMS::~Player_V2CMS() {
Common::StackLock lock(_mutex);
_mixer->stopHandle(_soundHandle);
- delete g_cmsEmu;
+ delete _cmsEmu;
}
void Player_V2CMS::setMusicVolume(int vol) {
}
-void Player_V2CMS::chainSound(int nr, byte *data) {
- int offset = _header_len + 10;
-
- _current_nr = nr;
- _current_data = data;
-
- for (int i = 0; i < 4; i++) {
- clear_channel(i);
-
- _channels[i].d.music_script_nr = nr;
- if (data) {
- _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
- if (_channels[i].d.next_cmd) {
- _channels[i].d.time_left = 1;
- }
- }
- }
- _music_timer = 0;
-}
-
-void Player_V2CMS::chainNextSound() {
- if (_next_nr) {
- chainSound(_next_nr, _next_data);
- _next_nr = 0;
- _next_data = 0;
- }
-}
-
void Player_V2CMS::stopAllSounds() {
Common::StackLock lock(_mutex);
@@ -702,275 +354,6 @@ int Player_V2CMS::getSoundStatus(int nr) const {
return _current_nr == nr || _next_nr == nr || _loadedMidiSong == nr;
}
-
-void Player_V2CMS::clear_channel(int i) {
- ChannelInfo *channel = &_channels[i];
- memset(channel, 0, sizeof(ChannelInfo));
-}
-
-int Player_V2CMS::getMusicTimer() {
- if (_isV3Game)
- return _music_timer;
- else
- return _channels[0].d.music_timer;
-}
-
-void Player_V2CMS::execute_cmd(ChannelInfo *channel) {
- uint16 value;
- int16 offset;
- uint8 *script_ptr;
- ChannelInfo * current_channel;
- ChannelInfo * dest_channel;
-
- current_channel = channel;
-
- if (channel->d.next_cmd == 0)
- goto check_stopped;
- script_ptr = &_current_data[channel->d.next_cmd];
-
- for (;;) {
- uint8 opcode = *script_ptr++;
- if (opcode >= 0xf8) {
- switch (opcode) {
- case 0xf8: // set hull curve
- debug(7, "channels[%d]: hull curve %2d",
- (uint)(channel - _channels), *script_ptr);
- channel->d.hull_curve = hull_offsets[*script_ptr / 2];
- script_ptr++;
- break;
-
- case 0xf9: // set freqmod curve
- debug(7, "channels[%d]: freqmod curve %2d",
- (uint)(channel - _channels), *script_ptr);
- channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
- channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
- script_ptr++;
- break;
-
- case 0xfd: // clear other channel
- value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
- debug(7, "clear channel %d", value);
- script_ptr += 2;
- // In Indy3, when traveling to Venice a command is
- // issued to clear channel 4. So we introduce a 4th
- // channel, which is never used. All OOB accesses are
- // mapped to this channel.
- //
- // The original game had room for 8 channels, but only
- // channels 0-3 are read, changes to other channels
- // had no effect.
- if (value >= ARRAYSIZE (_channels))
- value = 4;
- channel = &_channels[value];
- // fall through
-
- case 0xfa: // clear current channel
- if (opcode == 0xfa)
- debug(7, "clear channel");
- channel->d.next_cmd = 0;
- channel->d.base_freq = 0;
- channel->d.freq_delta = 0;
- channel->d.freq = 0;
- channel->d.volume = 0;
- channel->d.volume_delta = 0;
- channel->d.inter_note_pause = 0;
- channel->d.transpose = 0;
- channel->d.hull_curve = 0;
- channel->d.hull_offset = 0;
- channel->d.hull_counter = 0;
- channel->d.freqmod_table = 0;
- channel->d.freqmod_offset = 0;
- channel->d.freqmod_incr = 0;
- channel->d.freqmod_multiplier = 0;
- channel->d.freqmod_modulo = 0;
- break;
-
- case 0xfb: // ret from subroutine
- debug(7, "ret from sub");
- script_ptr = _retaddr;
- break;
-
- case 0xfc: // call subroutine
- offset = READ_LE_UINT16 (script_ptr);
- debug(7, "subroutine %d", offset);
- script_ptr += 2;
- _retaddr = script_ptr;
- script_ptr = _current_data + offset;
- break;
-
- case 0xfe: // loop music
- opcode = *script_ptr++;
- offset = READ_LE_UINT16 (script_ptr);
- script_ptr += 2;
- debug(7, "loop if %d to %d", opcode, offset);
- if (!channel->array[opcode / 2] || --channel->array[opcode/2])
- script_ptr += offset;
- break;
-
- case 0xff: // set parameter
- opcode = *script_ptr++;
- value = READ_LE_UINT16 (script_ptr);
- channel->array[opcode / 2] = value;
- debug(7, "channels[%d]: set param %2d = %5d",
- (uint)(channel - _channels), opcode, value);
- script_ptr += 2;
- if (opcode == 14) {
- /* tempo var */
- _ticks_per_music_timer = 125;
- }
- if (opcode == 0)
- goto end;
- break;
- }
- } else { // opcode < 0xf8
- for (;;) {
- int16 note, octave;
- int is_last_note;
- dest_channel = &_channels[(opcode >> 5) & 3];
-
- if (!(opcode & 0x80)) {
-
- int tempo = channel->d.tempo;
- if (!tempo)
- tempo = 1;
- channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
-
- note = *script_ptr++;
- is_last_note = note & 0x80;
- note &= 0x7f;
- if (note == 0x7f) {
- debug(8, "channels[%d]: pause %d",
- (uint)(channel - _channels), channel->d.time_left);
- goto end;
- }
- } else {
-
- channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
-
- if ((opcode & 0x10)) {
- debug(8, "channels[%d]: pause %d",
- (uint)(channel - _channels), channel->d.time_left);
- goto end;
- }
-
- is_last_note = 0;
- note = (*script_ptr++) & 0x7f;
- }
-
- debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
- (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0,
- note, (signed short) dest_channel->d.transpose, channel->d.time_left,
- dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
- dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
- is_last_note ? "last":"");
-
- uint16 myfreq;
- dest_channel->d.time_left = channel->d.time_left;
- dest_channel->d.note_length =
- channel->d.time_left - dest_channel->d.inter_note_pause;
- note += dest_channel->d.transpose;
- while (note < 0)
- note += 12;
- octave = note / 12;
- note = note % 12;
- dest_channel->d.hull_offset = 0;
- dest_channel->d.hull_counter = 1;
- if (dest_channel == &_channels[3]) {
- dest_channel->d.hull_curve = 196 + note * 12;
- myfreq = 384 - 64 * octave;
- } else {
- myfreq = pcjr_freq_table[note] >> octave;
- }
- dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
- if (is_last_note)
- goto end;
- opcode = *script_ptr++;
- }
- }
- }
-
-end:
- channel = current_channel;
- if (channel->d.time_left) {
- channel->d.next_cmd = script_ptr - _current_data;
- return;
- }
-
- channel->d.next_cmd = 0;
-
-check_stopped:
- int i;
- for (i = 0; i < 4; i++) {
- if (_channels[i].d.time_left)
- return;
- }
-
- _current_nr = 0;
- _current_data = 0;
- chainNextSound();
- return;
-}
-
-void Player_V2CMS::next_freqs(ChannelInfo *channel) {
- channel->d.volume += channel->d.volume_delta;
- channel->d.base_freq += channel->d.freq_delta;
-
- channel->d.freqmod_offset += channel->d.freqmod_incr;
- if (channel->d.freqmod_offset != 0)
- if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
- channel->d.freqmod_offset -= channel->d.freqmod_modulo;
-
- channel->d.freq =
- (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
- * (int) channel->d.freqmod_multiplier / 256
- + channel->d.base_freq;
-
- debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
- channel->d.base_freq, (int16)channel->d.freq_delta,
- channel->d.freqmod_table, channel->d.freqmod_offset,
- channel->d.freqmod_incr, channel->d.freqmod_multiplier,
- channel->d.freq);
-
- if (channel->d.note_length && !--channel->d.note_length) {
- channel->d.hull_offset = 16;
- channel->d.hull_counter = 1;
- }
-
- if (!--channel->d.time_left) {
- execute_cmd(channel);
- }
-
- if (channel->d.hull_counter && !--channel->d.hull_counter) {
- for (;;) {
- const int16 *hull_ptr = hulls
- + channel->d.hull_curve + channel->d.hull_offset / 2;
- if (hull_ptr[1] == -1) {
- channel->d.volume = hull_ptr[0];
- if (hull_ptr[0] == 0)
- channel->d.volume_delta = 0;
- channel->d.hull_offset += 4;
- } else {
- channel->d.volume_delta = hull_ptr[0];
- channel->d.hull_counter = hull_ptr[1];
- channel->d.hull_offset += 4;
- break;
- }
- }
- }
-}
-
-void Player_V2CMS::nextTick() {
- for (int i = 0; i < 4; i++) {
- if (!_channels[i].d.time_left)
- continue;
- next_freqs(&_channels[i]);
- }
- if (_music_timer_ctr++ >= _ticks_per_music_timer) {
- _music_timer_ctr = 0;
- _music_timer++;
- }
-}
-
void Player_V2CMS::processMidiData(uint ticks) {
byte *currentData = _midiData;
byte command = 0x00;
@@ -1031,7 +414,7 @@ int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) {
Common::StackLock lock(_mutex);
uint step = 1;
- int len = numSamples/2;
+ int len = numSamples / 2;
// maybe this needs a complete rewrite
do {
@@ -1061,7 +444,7 @@ int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) {
step = len;
if (step > (_next_tick >> FIXP_SHIFT))
step = (_next_tick >> FIXP_SHIFT);
- g_cmsEmu->readBuffer(buffer, step);
+ _cmsEmu->readBuffer(buffer, step);
buffer += 2 * step;
_next_tick -= step << FIXP_SHIFT;
} while (len -= step);
@@ -1105,27 +488,27 @@ void Player_V2CMS::playVoice() {
void Player_V2CMS::processChannel(Voice2 *channel) {
++_outputTableReady;
switch (channel->nextProcessState) {
- case PROCESS_RELEASE:
- processRelease(channel);
+ case PROCESS_RELEASE:
+ processRelease(channel);
break;
- case PROCESS_ATTACK:
- processAttack(channel);
+ case PROCESS_ATTACK:
+ processAttack(channel);
break;
- case PROCESS_DECAY:
- processDecay(channel);
+ case PROCESS_DECAY:
+ processDecay(channel);
break;
- case PROCESS_SUSTAIN:
- processSustain(channel);
+ case PROCESS_SUSTAIN:
+ processSustain(channel);
break;
- case PROCESS_VIBRATO:
- processVibrato(channel);
+ case PROCESS_VIBRATO:
+ processVibrato(channel);
break;
- default:
+ default:
break;
}
}
@@ -1207,8 +590,8 @@ void Player_V2CMS::offAllChannels() {
for (int cmsPort = 0x220, i = 0; i < 2; cmsPort += 2, ++i) {
for (int off = 0; off < 10; ++off) {
- g_cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]);
- g_cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]);
+ _cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]);
+ _cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]);
}
}*/
}
@@ -1397,32 +780,32 @@ void Player_V2CMS::play() {
// with the high nibble of the volumeReg value
// the right channels amplitude is set
// with the low value the left channels amplitude
- g_cmsEmu->portWrite(0x221, 0);
- g_cmsEmu->portWrite(0x220, volumeReg[0]);
- g_cmsEmu->portWrite(0x221, 1);
- g_cmsEmu->portWrite(0x220, volumeReg[1]);
- g_cmsEmu->portWrite(0x221, 2);
- g_cmsEmu->portWrite(0x220, volumeReg[2]);
- g_cmsEmu->portWrite(0x221, 3);
- g_cmsEmu->portWrite(0x220, volumeReg[3]);
- g_cmsEmu->portWrite(0x221, 8);
- g_cmsEmu->portWrite(0x220, freqReg[0]);
- g_cmsEmu->portWrite(0x221, 9);
- g_cmsEmu->portWrite(0x220, freqReg[1]);
- g_cmsEmu->portWrite(0x221, 10);
- g_cmsEmu->portWrite(0x220, freqReg[2]);
- g_cmsEmu->portWrite(0x221, 11);
- g_cmsEmu->portWrite(0x220, freqReg[3]);
- g_cmsEmu->portWrite(0x221, 0x10);
- g_cmsEmu->portWrite(0x220, octaveReg[0]);
- g_cmsEmu->portWrite(0x221, 0x11);
- g_cmsEmu->portWrite(0x220, octaveReg[1]);
- g_cmsEmu->portWrite(0x221, 0x14);
- g_cmsEmu->portWrite(0x220, freqEnable);
- g_cmsEmu->portWrite(0x221, 0x15);
- g_cmsEmu->portWrite(0x220, noiseEnable);
- g_cmsEmu->portWrite(0x221, 0x16);
- g_cmsEmu->portWrite(0x220, noiseGen);
+ _cmsEmu->portWrite(0x221, 0);
+ _cmsEmu->portWrite(0x220, volumeReg[0]);
+ _cmsEmu->portWrite(0x221, 1);
+ _cmsEmu->portWrite(0x220, volumeReg[1]);
+ _cmsEmu->portWrite(0x221, 2);
+ _cmsEmu->portWrite(0x220, volumeReg[2]);
+ _cmsEmu->portWrite(0x221, 3);
+ _cmsEmu->portWrite(0x220, volumeReg[3]);
+ _cmsEmu->portWrite(0x221, 8);
+ _cmsEmu->portWrite(0x220, freqReg[0]);
+ _cmsEmu->portWrite(0x221, 9);
+ _cmsEmu->portWrite(0x220, freqReg[1]);
+ _cmsEmu->portWrite(0x221, 10);
+ _cmsEmu->portWrite(0x220, freqReg[2]);
+ _cmsEmu->portWrite(0x221, 11);
+ _cmsEmu->portWrite(0x220, freqReg[3]);
+ _cmsEmu->portWrite(0x221, 0x10);
+ _cmsEmu->portWrite(0x220, octaveReg[0]);
+ _cmsEmu->portWrite(0x221, 0x11);
+ _cmsEmu->portWrite(0x220, octaveReg[1]);
+ _cmsEmu->portWrite(0x221, 0x14);
+ _cmsEmu->portWrite(0x220, freqEnable);
+ _cmsEmu->portWrite(0x221, 0x15);
+ _cmsEmu->portWrite(0x220, noiseEnable);
+ _cmsEmu->portWrite(0x221, 0x16);
+ _cmsEmu->portWrite(0x220, noiseGen);
}
void Player_V2CMS::playMusicChips(const MusicChip *table) {
@@ -1430,30 +813,30 @@ void Player_V2CMS::playMusicChips(const MusicChip *table) {
do {
cmsPort += 2;
- g_cmsEmu->portWrite(cmsPort+1, 0);
- g_cmsEmu->portWrite(cmsPort, table->ampl[0]);
- g_cmsEmu->portWrite(cmsPort+1, 1);
- g_cmsEmu->portWrite(cmsPort, table->ampl[1]);
- g_cmsEmu->portWrite(cmsPort+1, 2);
- g_cmsEmu->portWrite(cmsPort, table->ampl[2]);
- g_cmsEmu->portWrite(cmsPort+1, 3);
- g_cmsEmu->portWrite(cmsPort, table->ampl[3]);
- g_cmsEmu->portWrite(cmsPort+1, 8);
- g_cmsEmu->portWrite(cmsPort, table->freq[0]);
- g_cmsEmu->portWrite(cmsPort+1, 9);
- g_cmsEmu->portWrite(cmsPort, table->freq[1]);
- g_cmsEmu->portWrite(cmsPort+1, 10);
- g_cmsEmu->portWrite(cmsPort, table->freq[2]);
- g_cmsEmu->portWrite(cmsPort+1, 11);
- g_cmsEmu->portWrite(cmsPort, table->freq[3]);
- g_cmsEmu->portWrite(cmsPort+1, 0x10);
- g_cmsEmu->portWrite(cmsPort, table->octave[0]);
- g_cmsEmu->portWrite(cmsPort+1, 0x11);
- g_cmsEmu->portWrite(cmsPort, table->octave[1]);
- g_cmsEmu->portWrite(cmsPort+1, 0x14);
- g_cmsEmu->portWrite(cmsPort, 0x3F);
- g_cmsEmu->portWrite(cmsPort+1, 0x15);
- g_cmsEmu->portWrite(cmsPort, 0x00);
+ _cmsEmu->portWrite(cmsPort+1, 0);
+ _cmsEmu->portWrite(cmsPort, table->ampl[0]);
+ _cmsEmu->portWrite(cmsPort+1, 1);
+ _cmsEmu->portWrite(cmsPort, table->ampl[1]);
+ _cmsEmu->portWrite(cmsPort+1, 2);
+ _cmsEmu->portWrite(cmsPort, table->ampl[2]);
+ _cmsEmu->portWrite(cmsPort+1, 3);
+ _cmsEmu->portWrite(cmsPort, table->ampl[3]);
+ _cmsEmu->portWrite(cmsPort+1, 8);
+ _cmsEmu->portWrite(cmsPort, table->freq[0]);
+ _cmsEmu->portWrite(cmsPort+1, 9);
+ _cmsEmu->portWrite(cmsPort, table->freq[1]);
+ _cmsEmu->portWrite(cmsPort+1, 10);
+ _cmsEmu->portWrite(cmsPort, table->freq[2]);
+ _cmsEmu->portWrite(cmsPort+1, 11);
+ _cmsEmu->portWrite(cmsPort, table->freq[3]);
+ _cmsEmu->portWrite(cmsPort+1, 0x10);
+ _cmsEmu->portWrite(cmsPort, table->octave[0]);
+ _cmsEmu->portWrite(cmsPort+1, 0x11);
+ _cmsEmu->portWrite(cmsPort, table->octave[1]);
+ _cmsEmu->portWrite(cmsPort+1, 0x14);
+ _cmsEmu->portWrite(cmsPort, 0x3F);
+ _cmsEmu->portWrite(cmsPort+1, 0x15);
+ _cmsEmu->portWrite(cmsPort, 0x00);
++table;
} while ((cmsPort & 2) == 0);
}
diff --git a/engines/scumm/player_v2cms.h b/engines/scumm/player_v2cms.h
new file mode 100644
index 0000000000..fd939d8505
--- /dev/null
+++ b/engines/scumm/player_v2cms.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_PLAYER_V2CMS_H
+#define SCUMM_PLAYER_V2CMS_H
+
+#include "scumm/player_v2base.h" // for channel_data
+
+class CMSEmulator;
+
+namespace Scumm {
+
+/**
+ * Scumm V2 CMS/Gameblaster MIDI driver.
+ */
+class Player_V2CMS : public Player_V2Base {
+public:
+ Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer);
+ virtual ~Player_V2CMS();
+
+ // MusicEngine API
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+// virtual int getMusicTimer();
+ virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sampleRate; }
+
+protected:
+
+#include "common/pack-start.h" // START STRUCT PACKING
+ struct Voice {
+ byte attack;
+ byte decay;
+ byte sustain;
+ byte release;
+ byte octadd;
+ int16 vibrato;
+ int16 vibrato2;
+ int16 noise;
+ } PACKED_STRUCT;
+
+ struct Voice2 {
+ byte *amplitudeOutput;
+ byte *freqOutput;
+ byte *octaveOutput;
+
+ uint8 channel;
+ int8 sustainLevel;
+ int8 attackRate;
+ uint8 maxAmpl;
+ int8 decayRate;
+ int8 sustainRate;
+ int8 releaseRate;
+ int8 releaseTime;
+ int8 vibratoRate;
+ int8 vibratoDepth;
+
+ int8 curVibratoRate;
+ int8 curVibratoUnk;
+
+ int8 unkVibratoRate;
+ int8 unkVibratoDepth;
+
+ int8 unkRate;
+ int8 unkCount;
+
+ int nextProcessState;
+ int8 curVolume;
+ int8 curOctave;
+ int8 curFreq;
+
+ int8 octaveAdd;
+
+ int8 playingNote;
+ Voice2 *nextVoice;
+
+ byte chanNumber;
+ } PACKED_STRUCT;
+
+ struct MusicChip {
+ byte ampl[4];
+ byte freq[4];
+ byte octave[2];
+ } PACKED_STRUCT;
+#include "common/pack-end.h" // END STRUCT PACKING
+
+ Voice _cmsVoicesBase[16];
+ Voice2 _cmsVoices[8];
+ MusicChip _cmsChips[2];
+
+ int8 _tempo;
+ int8 _tempoSum;
+ byte _looping;
+ byte _octaveMask;
+ int16 _midiDelay;
+ Voice2 *_midiChannel[16];
+ byte _midiChannelUse[16];
+ byte *_midiData;
+ byte *_midiSongBegin;
+
+ int _loadedMidiSong;
+
+ byte _lastMidiCommand;
+ uint _outputTableReady;
+ byte _clkFrequenz;
+ byte _restart;
+ byte _curSno;
+
+ void loadMidiData(byte *data, int sound);
+ void play();
+
+ void processChannel(Voice2 *channel);
+ void processRelease(Voice2 *channel);
+ void processAttack(Voice2 *channel);
+ void processDecay(Voice2 *channel);
+ void processSustain(Voice2 *channel);
+ void processVibrato(Voice2 *channel);
+
+ void playMusicChips(const MusicChip *table);
+ void playNote(byte *&data);
+ void clearNote(byte *&data);
+ void offAllChannels();
+ void playVoice();
+ void processMidiData(uint ticks);
+
+ Voice2 *getFreeVoice();
+ Voice2 *getPlayVoice(byte param);
+
+ // from Player_V2
+protected:
+ CMSEmulator *_cmsEmu;
+
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
index d9a5e3414c..5aae59d987 100644
--- a/engines/scumm/resource.cpp
+++ b/engines/scumm/resource.cpp
@@ -1012,7 +1012,7 @@ void ResourceManager::freeResources() {
void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source) {
byte *alloced;
- int i, len;
+ int len;
_res->nukeResource(type, resindex);
@@ -1024,12 +1024,13 @@ void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source)
alloced = _res->createResource(type, resindex, len);
if (!source) {
- alloced[0] = fetchScriptByte();
- for (i = 1; i < len; i++)
- alloced[i] = *_scriptPointer++;
+ // Need to refresh the script pointer, since createResource may
+ // have caused the script resource to expire.
+ refreshScriptPointer();
+ memcpy(alloced, _scriptPointer, len);
+ _scriptPointer += len;
} else {
- for (i = 0; i < len; i++)
- alloced[i] = source[i];
+ memcpy(alloced, source, len);
}
}
diff --git a/engines/scumm/resource_v2.cpp b/engines/scumm/resource_v2.cpp
index e469c721b1..3dc3b4d14e 100644
--- a/engines/scumm/resource_v2.cpp
+++ b/engines/scumm/resource_v2.cpp
@@ -23,8 +23,6 @@
*
*/
-
-
#include "scumm/file.h"
#include "scumm/scumm_v2.h"
#include "scumm/resource.h"
@@ -175,24 +173,24 @@ void ScummEngine_v2::readIndexFile() {
switch (magic) {
case 0x0100:
- printf("Enhanced V2 game detected\n");
+ debug("Enhanced V2 game detected");
assert(_game.version == 2);
readEnhancedIndexFile();
break;
case 0x0A31:
- printf("Classic V1 game detected\n");
+ debug("Classic V1 game detected");
assert(_game.version == 1);
readClassicIndexFile();
break;
case 0x4643:
if (!(_game.platform == Common::kPlatformNES))
error("Use maniac target");
- printf("NES V1 game detected\n");
+ debug("NES V1 game detected");
assert(_game.version == 1);
readClassicIndexFile();
break;
case 0x132:
- printf("C64 V1 game detected\n");
+ debug("C64 V1 game detected");
if (_game.id == GID_MANIAC) {
assert(_game.version == 0);
} else {
@@ -201,7 +199,7 @@ void ScummEngine_v2::readIndexFile() {
readClassicIndexFile();
break;
case 0x032:
- printf("Apple II V1 game detected\n");
+ debug("Apple II V1 game detected");
assert(_game.version == 0);
readClassicIndexFile();
break;
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index b100e15604..7273f9f871 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -24,6 +24,7 @@
*/
#include "common/config-manager.h"
+#include "common/memstream.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/zlib.h"
@@ -45,6 +46,8 @@
#include "sound/mixer.h"
+#include "backends/audiocd/audiocd.h"
+
#include "graphics/thumbnail.h"
namespace Scumm {
@@ -377,10 +380,10 @@ bool ScummEngine::loadState(int slot, bool compat) {
return false;
}
- _engineStartTime = _system->getMillis() / 1000 - infos.playtime;
+ setTotalPlayTime(infos.playtime * 1000);
} else {
// start time counting
- _engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime();
}
// Due to a bug in scummvm up to and including 0.3.0, save games could be saved
@@ -796,7 +799,7 @@ void ScummEngine::saveInfos(Common::WriteStream* file) {
// still save old format for older versions
section.timeTValue = 0;
- section.playtime = _system->getMillis() / 1000 - _engineStartTime;
+ section.playtime = getTotalPlayTime() / 1000;
TimeDate curTime;
_system->getTimeAndDate(curTime);
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index d33ece7f6a..91e780bcd1 100644
--- a/engines/scumm/saveload.h
+++ b/engines/scumm/saveload.h
@@ -50,7 +50,7 @@ namespace Scumm {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 83
+#define CURRENT_VER 84
/**
* An auxillary macro, used to specify savegame versions. We use this instead
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 223e9822e2..eac2061560 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -339,7 +339,7 @@ void ScummEngine::runScriptNested(int script) {
_currentScript = script;
getScriptBaseAddress();
- getScriptEntryPoint();
+ resetScriptPointer();
executeScript();
if (vm.numNestedScripts != 0)
@@ -354,7 +354,7 @@ void ScummEngine::runScriptNested(int script) {
slot->status != ssDead && slot->freezeCount == 0) {
_currentScript = nest->slot;
getScriptBaseAddress();
- getScriptEntryPoint();
+ resetScriptPointer();
return;
}
}
@@ -440,24 +440,38 @@ void ScummEngine::getScriptBaseAddress() {
}
}
-
-void ScummEngine::getScriptEntryPoint() {
+void ScummEngine::resetScriptPointer() {
if (_currentScript == 0xFF)
return;
_scriptPointer = _scriptOrgPointer + vm.slot[_currentScript].offs;
}
+/**
+ * This method checks whether the resource that contains the active script
+ * moved, and if so, updates the script pointer accordingly.
+ *
+ * The script resource may have moved because it might have been garbage
+ * collected by ResourceManager::expireResources.
+ */
+void ScummEngine::refreshScriptPointer() {
+ if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
+ long oldoffs = _scriptPointer - _scriptOrgPointer;
+ getScriptBaseAddress();
+ _scriptPointer = _scriptOrgPointer + oldoffs;
+ }
+}
+
/** Execute a script - Read opcode, and execute it from the table */
void ScummEngine::executeScript() {
int c;
while (_currentScript != 0xFF) {
if (_showStack == 1) {
- printf("Stack:");
+ debugN("Stack:");
for (c = 0; c < _scummStackPos; c++) {
- printf(" %d", _vmStack[c]);
+ debugN(" %d", _vmStack[c]);
}
- printf("\n");
+ debugN("\n");
}
_opcode = fetchScriptByte();
if (_game.version > 2) // V0-V2 games didn't use the didexec flag
@@ -469,9 +483,9 @@ void ScummEngine::executeScript() {
getOpcodeDesc(_opcode));
if (_hexdumpScripts == true) {
for (c = -1; c < 15; c++) {
- printf(" %02x", *(_scriptPointer + c));
+ debugN(" %02x", *(_scriptPointer + c));
}
- printf("\n");
+ debugN("\n");
}
executeOpcode(_opcode);
@@ -492,20 +506,12 @@ const char *ScummEngine::getOpcodeDesc(byte i) {
}
byte ScummEngine::fetchScriptByte() {
- if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
- long oldoffs = _scriptPointer - _scriptOrgPointer;
- getScriptBaseAddress();
- _scriptPointer = _scriptOrgPointer + oldoffs;
- }
+ refreshScriptPointer();
return *_scriptPointer++;
}
uint ScummEngine::fetchScriptWord() {
- if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
- long oldoffs = _scriptPointer - _scriptOrgPointer;
- getScriptBaseAddress();
- _scriptPointer = _scriptOrgPointer + oldoffs;
- }
+ refreshScriptPointer();
uint a = READ_LE_UINT16(_scriptPointer);
_scriptPointer += 2;
return a;
@@ -516,11 +522,7 @@ int ScummEngine::fetchScriptWordSigned() {
}
uint ScummEngine::fetchScriptDWord() {
- if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
- long oldoffs = _scriptPointer - _scriptOrgPointer;
- getScriptBaseAddress();
- _scriptPointer = _scriptOrgPointer + oldoffs;
- }
+ refreshScriptPointer();
uint a = READ_LE_UINT32(_scriptPointer);
_scriptPointer += 4;
return a;
@@ -898,7 +900,7 @@ void ScummEngine::runAllScripts() {
if (vm.slot[i].cycle == cycle && vm.slot[i].status == ssRunning && !vm.slot[i].didexec) {
_currentScript = (byte)i;
getScriptBaseAddress();
- getScriptEntryPoint();
+ resetScriptPointer();
executeScript();
}
}
@@ -1231,22 +1233,26 @@ bool ScummEngine::isRoomScriptRunning(int script) const {
void ScummEngine::copyScriptString(byte *dst) {
int len = resStrLen(_scriptPointer) + 1;
- while (len--)
- *dst++ = fetchScriptByte();
+ memcpy(dst, _scriptPointer, len);
+ _scriptPointer += len;
+ dst += len;
*dst = 0;
}
-//
-// Given a pointer to a Scumm string, this function returns the total byte length
-// of the string data in that resource. To do so it has to understand certain
-// special characters embedded into the string. The reason for this function is that
-// sometimes this embedded data contains zero bytes, thus we can't just use strlen.
-//
-int ScummEngine::resStrLen(const byte *src) const {
+/**
+ * Given a pointer to a Scumm string, this function returns the total
+ * byte length of the string data in that resource. To do so it has to
+ * understand certain special characters embedded into the string. The
+ * reason for this function is that sometimes this embedded data
+ * contains zero bytes, thus we can't just use strlen.
+ */
+int ScummEngine::resStrLen(const byte *src) {
int num = 0;
byte chr;
- if (src == NULL)
+ if (src == NULL) {
+ refreshScriptPointer();
src = _scriptPointer;
+ }
while ((chr = *src++) != 0) {
num++;
if (_game.heversion <= 71 && chr == 0xFF) {
diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp
index cf44ee195e..d3f256c951 100644
--- a/engines/scumm/script_v0.cpp
+++ b/engines/scumm/script_v0.cpp
@@ -829,6 +829,8 @@ void ScummEngine_v0::o_setActorBitVar() {
// This flag causes the actor to stop moving (used by script #158, Green Tentacle 'Oomph!')
if (a->_miscflags & 0x40)
a->stopActorMoving();
+ if (a->_miscflags & 0x80)
+ a->setActorCostume(0);
debug(0, "o_setActorBitVar(%d, %d, %d)", act, mask, mod);
}
@@ -987,7 +989,10 @@ void ScummEngine_v0::o_setOwnerOf() {
void ScummEngine_v0::resetSentence(bool walking) {
_activeVerb = 13;
- if (!walking) {
+ // If the actor is walking, or the screen is a keypad (no sentence verbs/objects are drawn)
+ // Then reset all active objects (stops the radio crash, bug #3077966)
+ if (!walking || !(_userState & 32)) {
+ _v0ObjectFlag = 0;
_activeInventory = 0;
_activeObject = 0;
_activeObject2 = 0;
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index bc8446d16f..d3a2272a39 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -1174,6 +1174,8 @@ void ScummEngine_v2::o2_walkActorToObject() {
int obj;
Actor *a;
+ _v0ObjectFlag = 0;
+
a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject");
obj = getVarOrDirectWord(PARAM_2);
if (whereIsObject(obj) != WIO_NOT_FOUND) {
@@ -1182,6 +1184,7 @@ void ScummEngine_v2::o2_walkActorToObject() {
AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
x = r.x;
y = r.y;
+
a->startWalkActor(x, y, dir);
}
}
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index ea903fc108..d3e1ba43ef 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -1840,17 +1840,12 @@ void ScummEngine_v5::o5_roomOps() {
Common::InSaveFile *file = _saveFileMan->openForLoading(filename);
if (file != NULL) {
byte *ptr;
- int len = 256, cnt = 0;
- ptr = (byte *)malloc(len);
- while (ptr) {
- int r = file->read(ptr + cnt, len - cnt);
- cnt += r;
- if (cnt < len)
- break;
- len *= 2;
- ptr = (byte *)realloc(ptr, len);
- }
- ptr[cnt] = '\0';
+ const int len = file->size();
+ ptr = (byte *)malloc(len + 1);
+ assert(ptr);
+ int r = file->read(ptr, len);
+ assert(r == len);
+ ptr[len] = '\0';
loadPtrToResource(rtString, a, ptr);
free(ptr);
delete file;
@@ -2156,6 +2151,7 @@ void ScummEngine_v5::o5_stringOps() {
case 2: /* copystring */
a = getVarOrDirectByte(PARAM_1);
b = getVarOrDirectByte(PARAM_2);
+ assert(a != b);
_res->nukeResource(rtString, a);
ptr = getResourceAddress(rtString, b);
if (ptr)
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index c5841dfaf4..0226343df5 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -536,7 +536,18 @@ void ScummEngine_v6::o6_not() {
}
void ScummEngine_v6::o6_eq() {
- push(pop() == pop());
+ int a = pop();
+ int b = pop();
+
+ // WORKAROUND: Forces the game version string set via script 1 to be used in both Macintosh and Windows versions,
+ // when checking for save game compatibility. Allows saved games to be shared between Macintosh and Windows versions.
+ // The scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
+ if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) &&
+ a == 2 && b == 1) {
+ push(1);
+ } else {
+ push(a == b);
+ }
}
void ScummEngine_v6::o6_neq() {
@@ -1285,7 +1296,7 @@ void ScummEngine_v6::o6_loadRoomWithEgo() {
void ScummEngine_v6::o6_getRandomNumber() {
int rnd;
- rnd = _rnd.getRandomNumber(pop());
+ rnd = _rnd.getRandomNumber(ABS(pop()));
if (VAR_RANDOM_NR != 0xFF)
VAR(VAR_RANDOM_NR) = rnd;
push(rnd);
diff --git a/engines/scumm/script_v8.cpp b/engines/scumm/script_v8.cpp
index 4d552a9429..f2addff19f 100644
--- a/engines/scumm/script_v8.cpp
+++ b/engines/scumm/script_v8.cpp
@@ -766,7 +766,6 @@ void ScummEngine_v8::o8_actorOps() {
if (subOp == 0x7A) {
_curActor = pop();
- //printf("Setting current actor to %d\n", _curActor);
return;
}
@@ -937,7 +936,6 @@ void ScummEngine_v8::o8_verbOps() {
_curVerb = pop();
_curVerbSlot = getVerbSlot(_curVerb, 0);
assertRange(0, _curVerbSlot, _numVerbs - 1, "new verb slot");
- //printf("Setting current actor to %d\n", _curActor);
return;
}
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index b141b2d758..40eeba3663 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
/*
- This file was generated by the md5table tool on Sat Oct 02 14:51:12 2010
+ This file was generated by the md5table tool on Mon Oct 18 00:42:16 2010
DO NOT EDIT MANUALLY!
*/
@@ -132,7 +132,7 @@ static const MD5Table md5table[] = {
{ "2d4acbdcfd8e374c9da8c2e7303a5cd0", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformApple2GS },
{ "2d9d46f23cb07bbc90b8ad464d3e4ff8", "atlantis", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh },
- { "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Patched", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "2e8a1f76ea33bc5e04347646feee173d", "pajama3", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "2fe369ad70f52a8cf7ad6077ee64f81a", "loom", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "305d3dd57c96c65b017bc70c8c7cfb5e", "monkey", "CD", "CD", 8955, Common::DE_DEU, Common::kPlatformPC },
@@ -609,7 +609,7 @@ static const MD5Table md5table[] = {
{ "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", "", "Demo", 18354, Common::EN_ANY, Common::kPlatformUnknown },
{ "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", "Floppy", "Floppy", -1, Common::DE_DEU, Common::kPlatformPC },
{ "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows },
- { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "Demo", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh },
{ "f7711f9264d4d43c2a1518ec7c10a607", "pajama3", "", "", 79382, Common::EN_USA, Common::kPlatformUnknown },
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 2f25aeefba..e6110ee976 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -34,7 +34,7 @@
#include "engines/util.h"
#include "gui/message.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
#include "graphics/cursorman.h"
@@ -61,6 +61,7 @@
#include "scumm/player_pce.h"
#include "scumm/player_v1.h"
#include "scumm/player_v2.h"
+#include "scumm/player_v2cms.h"
#include "scumm/player_v2a.h"
#include "scumm/player_v3a.h"
#include "scumm/player_v4a.h"
@@ -74,6 +75,8 @@
#include "scumm/util.h"
#include "scumm/verbs.h"
+#include "backends/audiocd/audiocd.h"
+
#include "sound/mixer.h"
using Common::File;
@@ -136,7 +139,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
uint tmpVal;
tmpStr[0] = dr.md5[2*i];
tmpStr[1] = dr.md5[2*i+1];
- sscanf(tmpStr, "%x", &tmpVal);
+ int res = sscanf(tmpStr, "%x", &tmpVal);
+ assert(res == 1);
_gameMD5[i] = (byte)tmpVal;
}
@@ -145,6 +149,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
// Init all vars
_v0ObjectIndex = false;
_v0ObjectInInventory = false;
+ _v0ObjectFlag = 0;
_imuse = NULL;
_imuseDigital = NULL;
_musicEngine = NULL;
@@ -209,7 +214,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_saveLoadSlot = 0;
_lastSaveTime = 0;
_saveTemporaryState = false;
- memset(_saveLoadFileName, 0, sizeof(_saveLoadFileName));
memset(_saveLoadName, 0, sizeof(_saveLoadName));
memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));
_scriptPointer = NULL;
@@ -281,6 +285,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
_townsScreen = 0;
#endif
+ _cjkFont = 0;
+ _cjkChar = 0;
_shadowPalette = NULL;
_shadowPaletteSize = 0;
memset(_currentPalette, 0, sizeof(_currentPalette));
@@ -481,7 +487,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
if (ConfMan.getBool("demo_mode"))
_game.features |= GF_DEMO;
if (ConfMan.hasKey("nosubtitles")) {
- printf("Configuration key 'nosubtitles' is deprecated. Use 'subtitles' instead\n");
+ // We replaced nosubtitles *ages* ago. Just convert it silently
+ debug("Configuration key 'nosubtitles' is deprecated. Converting to 'subtitles'");
if (!ConfMan.hasKey("subtitles"))
ConfMan.setBool("subtitles", !ConfMan.getBool("nosubtitles"));
}
@@ -631,6 +638,7 @@ ScummEngine::~ScummEngine() {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
delete _townsScreen;
#endif
+ delete _cjkFont;
delete _debugger;
@@ -1642,6 +1650,10 @@ void ScummEngine_v90he::resetScumm() {
_logicHE = new LogicHEsoccer(this);
break;
+ case GID_BASEBALL2001:
+ _logicHE = new LogicHEbaseball2001(this);
+ break;
+
case GID_BASKETBALL:
_logicHE = new LogicHEbasketball(this);
break;
@@ -1918,7 +1930,7 @@ int ScummEngine::getTalkSpeed() {
#pragma mark -
Common::Error ScummEngine::go() {
- _engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime();
// If requested, load a save game instead of running the boot script
if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveTemporaryState)) {
@@ -2496,10 +2508,6 @@ void ScummEngine::startManiac() {
void ScummEngine::pauseEngineIntern(bool pause) {
if (pause) {
- // Record start of the pause, so that we can later
- // adjust _engineStartTime accordingly.
- _pauseStartTime = _system->getMillis();
-
// Pause sound & video
_oldSoundsPaused = _sound->_soundsPaused;
_sound->pauseSounds(true);
@@ -2517,10 +2525,6 @@ void ScummEngine::pauseEngineIntern(bool pause) {
// Resume sound & video
_sound->pauseSounds(_oldSoundsPaused);
-
- // Adjust engine start time
- _engineStartTime += (_system->getMillis() - _pauseStartTime) / 1000;
- _pauseStartTime = 0;
}
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index aef5cfbec7..0a513b6068 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -36,6 +36,7 @@
#include "common/rect.h"
#include "common/str.h"
#include "graphics/surface.h"
+#include "graphics/sjis.h"
#include "scumm/gfx.h"
#include "scumm/detection.h"
@@ -66,10 +67,14 @@ namespace Common {
/**
* This is the namespace of the SCUMM engine.
*
- * Status of this engine: ???
+ * Status of this engine:
+ * Complete support for all SCUMM based LucasArts adventures.
+ * Complete support for many Humongous Entertainment games,
+ * but for some of the newer ones, this is still work in progress.
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Classic 2D LucasArts adventures
+ * - numerous Humongous Entertainment games
*/
namespace Scumm {
@@ -248,6 +253,7 @@ enum ScummGameId {
GID_FUNSHOP, // Used for all three funshops
GID_FOOTBALL,
GID_SOCCER,
+ GID_BASEBALL2001,
GID_BASKETBALL,
GID_MOONBASE,
GID_HECUP // CUP demos
@@ -587,6 +593,7 @@ protected:
bool _v0ObjectIndex; // V0 Use object index, instead of object number
bool _v0ObjectInInventory; // V0 Use object number from inventory
+ byte _v0ObjectFlag;
/* Global resource tables */
int _numVariables, _numBitVariables, _numLocalObjects;
@@ -654,7 +661,7 @@ protected:
byte _saveLoadFlag, _saveLoadSlot;
uint32 _lastSaveTime;
bool _saveTemporaryState;
- char _saveLoadFileName[32];
+ Common::String _saveLoadFileName;
char _saveLoadName[32];
bool saveState(Common::OutSaveFile *out, bool writeHeader = true);
@@ -693,9 +700,6 @@ protected:
void saveInfos(Common::WriteStream* file);
static bool loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff);
- int32 _engineStartTime;
- int32 _pauseStartTime;
-
protected:
/* Script VM - should be in Script class */
uint32 _localScriptOffsets[1024];
@@ -748,9 +752,10 @@ protected:
void stopObjectScript(int script);
void getScriptBaseAddress();
- void getScriptEntryPoint();
+ void resetScriptPointer();
int getVerbEntrypoint(int obj, int entry);
+ void refreshScriptPointer();
byte fetchScriptByte();
virtual uint fetchScriptWord();
virtual int fetchScriptWordSigned();
@@ -770,7 +775,7 @@ protected:
void endOverride();
void copyScriptString(byte *dst);
- int resStrLen(const byte *src) const;
+ int resStrLen(const byte *src);
void doSentence(int c, int b, int a);
/* Should be in Resource class */
@@ -1417,6 +1422,8 @@ public:
bool towns_isRectInStringBox(int x1, int y1, int x2, int y2);
byte _townsPaletteFlags;
byte _townsCharsetColorMap[16];
+ Graphics::FontSJIS *_cjkFont;
+ uint16 _cjkChar;
protected:
void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);
diff --git a/engines/scumm/smush/codec47.cpp b/engines/scumm/smush/codec47.cpp
index 62bc0bb098..333fdabccf 100644
--- a/engines/scumm/smush/codec47.cpp
+++ b/engines/scumm/smush/codec47.cpp
@@ -301,9 +301,11 @@ void Codec47Decoder::makeTables47(int width) {
int32 a, c, d;
int16 tmp;
- for (int l = 0; l < 512; l += 2) {
+ for (int l = 0; l < ARRAYSIZE(codec47_table); l += 2) {
_table[l / 2] = (int16)(codec47_table[l + 1] * width + codec47_table[l]);
}
+ // Note: _table[255] is never inited; but since only the first 0xF8
+ // entries of it are used anyway, this doesn't matter.
a = 0;
c = 0;
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index af7bc5f295..ee8825d946 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -36,6 +36,8 @@
#include "scumm/sound.h"
#include "scumm/util.h"
+#include "backends/audiocd/audiocd.h"
+
#include "sound/decoders/adpcm.h"
#include "sound/decoders/flac.h"
#include "sound/mididrv.h"
@@ -596,7 +598,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
#ifdef USE_MAD
{
assert(size > 0);
- Common::MemoryReadStream *tmp = _sfxFile->readStream(size);
+ Common::SeekableReadStream *tmp = _sfxFile->readStream(size);
assert(tmp);
input = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
}
@@ -606,7 +608,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
#ifdef USE_VORBIS
{
assert(size > 0);
- Common::MemoryReadStream *tmp = _sfxFile->readStream(size);
+ Common::SeekableReadStream *tmp = _sfxFile->readStream(size);
assert(tmp);
input = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
}
@@ -616,7 +618,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
#ifdef USE_FLAC
{
assert(size > 0);
- Common::MemoryReadStream *tmp = _sfxFile->readStream(size);
+ Common::SeekableReadStream *tmp = _sfxFile->readStream(size);
assert(tmp);
input = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
}
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 5eb2b9d159..4b09547c8c 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -601,7 +601,12 @@ void ScummEngine::CHARSET_1() {
} else if (!(_game.platform == Common::kPlatformFMTowns) && _string[0].height) {
_nextTop += _string[0].height;
} else {
+ bool useCJK = _useCJKMode;
+ // SCUMM5 FM-Towns doesn't use the height of the ROM font here.
+ if (_game.platform == Common::kPlatformFMTowns && _game.version == 5)
+ _useCJKMode = false;
_nextTop += _charset->getFontHeight();
+ _useCJKMode = useCJK;
}
if (_game.version > 3) {
// FIXME: is this really needed?
@@ -634,9 +639,7 @@ void ScummEngine::CHARSET_1() {
#endif
} else {
if (c & 0x80 && _useCJKMode) {
- if (_language == Common::JA_JPN && !checkSJISCode(c)) {
- c = 0x20; //not in S-JIS
- } else {
+ if (checkSJISCode(c)) {
byte *buffer = _charsetBuffer + _charsetBufPos;
c += *buffer++ * 256; //LE
_charsetBufPos = buffer - _charsetBuffer;
@@ -993,11 +996,8 @@ void ScummEngine::drawString(int a, const byte *msg) {
}
}
if (c & 0x80 && _useCJKMode) {
- if (_language == Common::JA_JPN && !checkSJISCode(c)) {
- c = 0x20; //not in S-JIS
- } else {
+ if (checkSJISCode(c))
c += buf[i++] * 256;
- }
}
_charset->printChar(c, true);
_charset->_blitAlso = false;
@@ -1021,6 +1021,7 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
uint num = 0;
uint32 val;
byte chr;
+ byte lastChr = 0;
const byte *src;
byte *end;
byte transBuf[384];
@@ -1128,11 +1129,12 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
} else if (_game.id == GID_DIG && (chr == 1 || chr == 2 || chr == 3 || chr == 8)) {
// Skip these characters
} else {
- if (!(chr == '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) ||
- (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN))
- {
+ if ((chr != '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) ||
+ (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) ||
+ (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN && checkSJISCode(lastChr))) {
*dst++ = chr;
}
+ lastChr = chr;
}
// Check for a buffer overflow
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 77181c0b55..c443f98bc6 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -167,11 +167,6 @@ void ScummEngine_v0::switchActor(int slot) {
if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2)
return;
- // verbs disabled for the current actor
- ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "switchActor");
- if (a->_miscflags & 0x40)
- return;
-
VAR(VAR_EGO) = VAR(97 + slot);
resetVerbs();
actorFollowCamera(VAR(VAR_EGO));
@@ -323,7 +318,7 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
}
}
- if (new_box != _mouseOverBoxV2) {
+ if ((new_box != _mouseOverBoxV2) || (_game.version == 0)) {
if (_mouseOverBoxV2 != -1) {
rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect;
@@ -524,9 +519,8 @@ void ScummEngine_v2::handleMouseOver(bool updateInventory) {
}
void ScummEngine_v0::handleMouseOver(bool updateInventory) {
- ScummEngine_v2::handleMouseOver(updateInventory);
-
drawSentence();
+ ScummEngine_v2::handleMouseOver(updateInventory);
}
#ifdef ENABLE_HE
@@ -727,7 +721,7 @@ void ScummEngine_v2::checkExecVerbs() {
}
void ScummEngine_v0::runObject(int obj, int entry) {
- int prev = _v0ObjectInInventory;
+ bool prev = _v0ObjectInInventory;
if (getVerbEntrypoint(obj, entry) == 0) {
// If nothing was found, attempt to find the 'WHAT-IS' verb script
@@ -978,6 +972,7 @@ bool ScummEngine_v0::verbExec() {
// We acted on an inventory item
if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) {
_v0ObjectInInventory = true;
+ _activeObject = _activeInventory;
runObject(_activeInventory, _activeVerb);
_verbExecuting = false;
@@ -1036,7 +1031,7 @@ bool ScummEngine_v0::verbExec() {
}
void ScummEngine_v0::checkExecVerbs() {
- Actor *a = derefActor(VAR(VAR_EGO), "checkExecVerbs");
+ ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
VirtScreen *zone = findVirtScreen(_mouse.y);
// Is a verb currently executing
@@ -1151,27 +1146,28 @@ void ScummEngine_v0::checkExecVerbs() {
obj = 0;
objIdx = 0;
}
+
+ if (a->_miscflags & 0x80) {
+ if (_activeVerb != 7 && over != 7) {
+ _activeVerb = 0;
+ over = 0;
+ }
+ }
// Handle New Kid verb options
if (_activeVerb == 7 || over == 7) {
// Disable New-Kid (in the secret lab)
if (_currentMode == 2 || _currentMode == 0)
return;
-
- if (!(((ActorC64 *)a)->_miscflags & 0x80)) {
- if (_activeVerb != 7) {
- _activeVerb = over;
- over = 0;
- }
- }
- if (over) {
+ if (_activeVerb == 7 && over) {
_activeVerb = 13;
switchActor(_verbs[over].verbid - 1);
return;
}
setNewKidVerbs();
+ _activeVerb = 7;
return;
}
@@ -1187,7 +1183,7 @@ void ScummEngine_v0::checkExecVerbs() {
if (zone->number == kMainVirtScreen) {
// Ignore verbs?
- if (((ActorC64 *)a)->_miscflags & 0x40) {
+ if (a->_miscflags & 0x40) {
resetSentence(false);
return;
}
diff --git a/engines/sky/disk.cpp b/engines/sky/disk.cpp
index 90b55133ed..5ad0c3eb19 100644
--- a/engines/sky/disk.cpp
+++ b/engines/sky/disk.cpp
@@ -59,7 +59,7 @@ Disk::Disk() {
if (!_dataDiskHandle->isOpen())
error("Error opening %s", dataFilename);
- printf("Found BASS version v0.0%d (%d dnr entries)\n", determineGameVersion(), _dinnerTableEntries);
+ debug("Found BASS version v0.0%d (%d dnr entries)", determineGameVersion(), _dinnerTableEntries);
memset(_buildList, 0, 60 * 2);
memset(_loadedFilesList, 0, 60 * 4);
diff --git a/engines/sky/logic.cpp b/engines/sky/logic.cpp
index e19cbb27e8..1c1e2c9682 100644
--- a/engines/sky/logic.cpp
+++ b/engines/sky/logic.cpp
@@ -2510,7 +2510,7 @@ bool Logic::fnUnPauseFx(uint32 a, uint32 b, uint32 c) {
}
bool Logic::fnPrintf(uint32 a, uint32 b, uint32 c) {
- printf("fnPrintf: %d\n", a);
+ debug("fnPrintf(%d, %d, %d)", a, b, c);
return true;
}
diff --git a/engines/sky/sky.h b/engines/sky/sky.h
index 7eff8c662a..58c9d1de11 100644
--- a/engines/sky/sky.h
+++ b/engines/sky/sky.h
@@ -35,8 +35,8 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Beneath a Steel Sky
*/
namespace Sky {
diff --git a/engines/sword1/console.cpp b/engines/sword1/console.cpp
new file mode 100644
index 0000000000..7a1c087dd5
--- /dev/null
+++ b/engines/sword1/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sword1/console.h"
+#include "sword1/sword1.h"
+
+namespace Sword1 {
+
+SwordConsole::SwordConsole(SwordEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+SwordConsole::~SwordConsole() {
+}
+
+void SwordConsole::preEnter() {
+}
+
+void SwordConsole::postEnter() {
+}
+
+} // End of namespace Sword
diff --git a/engines/sword1/console.h b/engines/sword1/console.h
new file mode 100644
index 0000000000..09d197363e
--- /dev/null
+++ b/engines/sword1/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SWORD1_CONSOLE_H
+#define SWORD1_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Sword1 {
+
+class SwordEngine;
+
+class SwordConsole : public GUI::Debugger {
+public:
+ SwordConsole(SwordEngine *vm);
+ virtual ~SwordConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ SwordEngine *_vm;
+};
+
+} // End of namespace Sword1
+
+#endif
diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp
index 8d9ca85829..aee49c4b60 100644
--- a/engines/sword1/control.cpp
+++ b/engines/sword1/control.cpp
@@ -1118,8 +1118,7 @@ void Control::saveGameToFile(uint8 slot) {
outf->writeUint32BE(saveDate);
outf->writeUint16BE(saveTime);
- uint32 currentTime = _system->getMillis() / 1000;
- outf->writeUint32BE(currentTime - SwordEngine::_systemVars.engineStartTime);
+ outf->writeUint32BE(g_engine->getTotalPlayTime() / 1000);
_objMan->saveLiveList(liveBuf);
for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
@@ -1181,10 +1180,9 @@ bool Control::restoreGameFromFile(uint8 slot) {
inf->readUint16BE(); // save time
if (saveVersion < 2) { // Before version 2 we didn't had play time feature
- SwordEngine::_systemVars.engineStartTime = _system->getMillis() / 1000; // Start counting
+ g_engine->setTotalPlayTime(0);
} else {
- uint32 currentTime = _system->getMillis() / 1000;
- SwordEngine::_systemVars.engineStartTime = currentTime - inf->readUint32BE(); // Engine start time
+ g_engine->setTotalPlayTime(inf->readUint32BE() * 1000);
}
_restoreBuf = (uint8*)malloc(
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index e51a3d6a8d..855c7d1f83 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -33,6 +33,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
#include "engines/metaengine.h"
@@ -309,12 +310,9 @@ SaveStateDescriptor SwordMetaEngine::querySaveMetaInfos(const char *target, int
desc.setSaveTime(hour, minutes);
if (versionSave > 1) {
- minutes = playTime / 60;
- hour = minutes / 60;
- minutes %= 60;
- desc.setPlayTime(hour, minutes);
+ desc.setPlayTime(playTime * 1000);
} else { //We have no playtime data
- desc.setPlayTime(0, 0);
+ desc.setPlayTime(0);
}
delete in;
diff --git a/engines/sword1/module.mk b/engines/sword1/module.mk
index 1dbff19464..a19fcbfab9 100644
--- a/engines/sword1/module.mk
+++ b/engines/sword1/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/sword1
MODULE_OBJS := \
animation.o \
+ console.o \
control.o \
debug.o \
detection.o \
diff --git a/engines/sword1/resman.cpp b/engines/sword1/resman.cpp
index 41f952c3f4..2bad8a79a7 100644
--- a/engines/sword1/resman.cpp
+++ b/engines/sword1/resman.cpp
@@ -36,7 +36,7 @@
#include "sword1/swordres.h"
#include "gui/message.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
namespace Sword1 {
void guiFatalError(char *msg) {
@@ -332,10 +332,13 @@ Common::File *ResMan::resFile(uint32 id) {
Clu *closeClu = _openCluStart;
_openCluStart = _openCluStart->nextOpen;
- closeClu->file->close();
- delete closeClu->file;
- closeClu->file = NULL;
- closeClu->nextOpen = NULL;
+ if (closeClu) {
+ if (closeClu->file)
+ closeClu->file->close();
+ delete closeClu->file;
+ closeClu->file = NULL;
+ closeClu->nextOpen = NULL;
+ }
_openClus--;
}
}
diff --git a/engines/sword1/screen.cpp b/engines/sword1/screen.cpp
index b07acf9a0b..9fa808d561 100644
--- a/engines/sword1/screen.cpp
+++ b/engines/sword1/screen.cpp
@@ -886,8 +886,8 @@ uint8* Screen::psxBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint
// needed because some psx backgrounds are half width and half height
uint8* Screen::psxShrinkedBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint32 bakYres) {
- uint32 xresInTiles = (bakXres / 2) % 16 ? (bakXres / 32) + 1 : (bakXres / 32);
- uint32 yresInTiles = (bakYres / 2) % 16 ? (bakYres / 32) + 1 : (bakYres / 32);
+ uint32 xresInTiles = ((bakXres / 2) % 16) ? (bakXres / 32) + 1 : (bakXres / 32);
+ uint32 yresInTiles = ((bakYres / 2) % 16) ? (bakYres / 32) + 1 : (bakYres / 32);
uint32 totTiles = xresInTiles * yresInTiles;
uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid
uint32 tileXpos = 0;
diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp
index 55600cfb63..92bed1b624 100644
--- a/engines/sword1/sound.cpp
+++ b/engines/sword1/sound.cpp
@@ -29,6 +29,7 @@
#include "common/util.h"
#include "common/events.h"
#include "common/EventRecorder.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "sword1/sound.h"
@@ -366,7 +367,7 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
}
} else if (_cowMode == CowPSX && sampleSize != 0xffffffff) {
_cowFile.seek(index * 2048);
- Common::MemoryReadStream *tmp = _cowFile.readStream(sampleSize);
+ Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
assert(tmp);
stream = Audio::makeVagStream(tmp);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
@@ -379,7 +380,7 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
#ifdef USE_FLAC
else if (_cowMode == CowFLAC) {
_cowFile.seek(index);
- Common::MemoryReadStream *tmp = _cowFile.readStream(sampleSize);
+ Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
assert(tmp);
stream = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
@@ -393,7 +394,7 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
#ifdef USE_VORBIS
else if (_cowMode == CowVorbis) {
_cowFile.seek(index);
- Common::MemoryReadStream *tmp = _cowFile.readStream(sampleSize);
+ Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
assert(tmp);
stream = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
@@ -407,7 +408,7 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
#ifdef USE_MAD
else if (_cowMode == CowMP3) {
_cowFile.seek(index);
- Common::MemoryReadStream *tmp = _cowFile.readStream(sampleSize);
+ Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize);
assert(tmp);
stream = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan);
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
index 0d4f5b7445..537720401d 100644
--- a/engines/sword1/sword1.cpp
+++ b/engines/sword1/sword1.cpp
@@ -41,7 +41,7 @@
#include "engines/util.h"
#include "gui/message.h"
-#include "gui/GuiManager.h"
+#include "gui/gui-manager.h"
namespace Sword1 {
@@ -66,6 +66,8 @@ SwordEngine::SwordEngine(OSystem *syst)
SearchMan.addSubDirectoryMatching(gameDataDir, "smackshi");
SearchMan.addSubDirectoryMatching(gameDataDir, "english");//PSX Demo
SearchMan.addSubDirectoryMatching(gameDataDir, "italian");//PSX Demo
+
+ _console = new SwordConsole(this);
}
SwordEngine::~SwordEngine() {
@@ -78,6 +80,7 @@ SwordEngine::~SwordEngine() {
delete _mouse;
delete _objectMan;
delete _resMan;
+ delete _console;
}
Common::Error SwordEngine::init() {
@@ -563,7 +566,7 @@ void SwordEngine::checkCdFiles() { // check if we're running from cd, hdd or wha
Common::Error SwordEngine::go() {
_control->checkForOldSaveGames();
- SwordEngine::_systemVars.engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime(0);
uint16 startPos = ConfMan.getInt("boot_param");
_control->readSavegameDescriptions();
@@ -678,6 +681,13 @@ uint8 SwordEngine::mainLoop() {
if (retCode == CONTROL_NOTHING_DONE)
_screen->fullRefresh();
}
+
+ // Check for Debugger Activation
+ if (_keyPressed.hasFlags(Common::KBD_CTRL) && _keyPressed.keycode == Common::KEYCODE_d) {
+ this->getDebugger()->attach();
+ this->getDebugger()->onFrame();
+ }
+
_mouseState = 0;
_keyPressed.reset();
} while ((Logic::_scriptVars[SCREEN] == Logic::_scriptVars[NEW_SCREEN]) && (retCode == 0) && (!shouldQuit()));
diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h
index a0497847d5..592d2da6f4 100644
--- a/engines/sword1/sword1.h
+++ b/engines/sword1/sword1.h
@@ -30,14 +30,15 @@
#include "common/events.h"
#include "common/util.h"
#include "sword1/sworddefs.h"
+#include "sword1/console.h"
/**
* This is the namespace of the Sword1 engine.
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Broken Sword: The Shadow of the Templars
*/
namespace Sword1 {
@@ -74,7 +75,6 @@ struct SystemVars {
uint8 showText;
uint8 language;
bool isDemo;
- uint32 engineStartTime; // Used for playtime
Common::Platform platform;
};
@@ -107,6 +107,8 @@ protected:
virtual bool hasFeature(EngineFeature f) const;
virtual void syncSoundSettings();
+ GUI::Debugger *getDebugger() { return _console; }
+
Common::Error loadGameState(int slot);
bool canLoadGameStateCurrently();
Common::Error saveGameState(int slot, const char *desc);
@@ -122,6 +124,8 @@ private:
void reinitRes(); //Reinits the resources after a GMM load
+ SwordConsole *_console;
+
uint8 mainLoop();
Common::Point _mouseCoord;
diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp
index 10895b2ec1..f664e784d5 100644
--- a/engines/sword2/animation.cpp
+++ b/engines/sword2/animation.cpp
@@ -398,10 +398,4 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *s
return NULL;
}
-void MoviePlayer::pauseMovie(bool pause) {
- if (_bgSoundHandle) {
- _snd->pauseHandle(*_bgSoundHandle, pause);
- }
-}
-
} // End of namespace Sword2
diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h
index ee32b1d5f2..4796d39097 100644
--- a/engines/sword2/animation.h
+++ b/engines/sword2/animation.h
@@ -76,7 +76,6 @@ public:
bool load(const char *name);
void play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut);
- void pauseMovie(bool pause);
protected:
Sword2Engine *_vm;
diff --git a/engines/sword2/console.cpp b/engines/sword2/console.cpp
index 4bf7c0da19..6f4665a34c 100644
--- a/engines/sword2/console.cpp
+++ b/engines/sword2/console.cpp
@@ -26,6 +26,7 @@
*/
+#include "common/memstream.h"
#include "common/rect.h"
#include "common/system.h"
@@ -237,13 +238,15 @@ bool Debugger::Cmd_Mem(int argc, const char **argv) {
break;
}
- DebugPrintf("%9ld %-3d %-4d %-20s %s\n", blocks[i]->size, blocks[i]->id, blocks[i]->uid, type, _vm->_resman->fetchName(blocks[i]->ptr));
+ DebugPrintf("%9d %-3d %-4d %-20s %s\n",
+ blocks[i]->size, blocks[i]->id, blocks[i]->uid,
+ type, _vm->_resman->fetchName(blocks[i]->ptr));
}
free(blocks);
DebugPrintf("---------------------------------------------------------------------------\n");
- DebugPrintf("%9ld\n", _vm->_memory->getTotAlloc());
+ DebugPrintf("%9d\n", _vm->_memory->getTotAlloc());
return true;
}
diff --git a/engines/sword2/controls.cpp b/engines/sword2/controls.cpp
index 7428cbb534..c55cc72493 100644
--- a/engines/sword2/controls.cpp
+++ b/engines/sword2/controls.cpp
@@ -180,12 +180,12 @@ void FontRendererGui::fetchText(uint32 textId, byte *buf) {
byte *data = _vm->fetchTextLine(_vm->_resman->openResource(textId / SIZE), textId & 0xffff);
int i;
- for (i = 0; data[i + 2]; i++) {
- if (buf)
+ if (buf) {
+ for (i = 0; data[i + 2]; i++)
buf[i] = data[i + 2];
+ buf[i] = 0;
}
- buf[i] = 0;
_vm->_resman->closeResource(textId / SIZE);
}
diff --git a/engines/sword2/debug.cpp b/engines/sword2/debug.cpp
index 8f9f54ad5c..cb3c3b6a30 100644
--- a/engines/sword2/debug.cpp
+++ b/engines/sword2/debug.cpp
@@ -120,15 +120,6 @@ void Debugger::buildDebugText() {
makeDebugTextBlock(buf, 0, 105);
}
-#ifdef SWORD2_DEBUG
- // speed-up indicator
-
- if (_vm->_renderSkip) { // see sword2.cpp
- sprintf(buf, "SKIPPING FRAMES FOR SPEED-UP!");
- makeDebugTextBlock(buf, 0, 120);
- }
-#endif
-
// debug info at top of screen - enabled/disabled as one complete unit
if (_displayTime) {
diff --git a/engines/sword2/header.cpp b/engines/sword2/header.cpp
new file mode 100644
index 0000000000..6ab88d159d
--- /dev/null
+++ b/engines/sword2/header.cpp
@@ -0,0 +1,364 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sword2/header.h"
+#include "sword2/object.h"
+#include "sword2/screen.h"
+#include "sword2/sword2.h"
+
+#include "common/memstream.h"
+#include "common/endian.h"
+
+namespace Sword2 {
+
+void ResHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ fileType = readS.readByte();
+ compType = readS.readByte();
+ compSize = readS.readUint32LE();
+ decompSize = readS.readUint32LE();
+ readS.read(name, NAME_LEN);
+}
+
+void ResHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeByte(fileType);
+ writeS.writeByte(compType);
+ writeS.writeUint32LE(compSize);
+ writeS.writeUint32LE(decompSize);
+ writeS.write(name, NAME_LEN);
+}
+
+void AnimHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ if (Sword2Engine::isPsx()) {
+ noAnimFrames = readS.readUint16LE();
+ feetStartX = readS.readUint16LE();
+ feetStartY = readS.readUint16LE();
+ feetEndX = readS.readUint16LE();
+ feetEndY = readS.readUint16LE();
+ blend = readS.readUint16LE();
+ runTimeComp = readS.readByte();
+ feetStartDir = readS.readByte();
+ feetEndDir = readS.readByte();
+ } else {
+ runTimeComp = readS.readByte();
+ noAnimFrames = readS.readUint16LE();
+ feetStartX = readS.readUint16LE();
+ feetStartY = readS.readUint16LE();
+ feetStartDir = readS.readByte();
+ feetEndX = readS.readUint16LE();
+ feetEndY = readS.readUint16LE();
+ feetEndDir = readS.readByte();
+ blend = readS.readUint16LE();
+ }
+}
+
+void AnimHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeByte(runTimeComp);
+ writeS.writeUint16LE(noAnimFrames);
+ writeS.writeUint16LE(feetStartX);
+ writeS.writeUint16LE(feetStartY);
+ writeS.writeByte(feetStartDir);
+ writeS.writeUint16LE(feetEndX);
+ writeS.writeUint16LE(feetEndY);
+ writeS.writeByte(feetEndDir);
+ writeS.writeUint16LE(blend);
+}
+
+int CdtEntry::size() {
+ if (Sword2Engine::isPsx())
+ return 12;
+ else
+ return 9;
+}
+
+void CdtEntry::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ if (Sword2Engine::isPsx()) {
+ readS.readByte(); // Skip a byte in psx version
+ x = readS.readUint16LE();
+ y = readS.readUint16LE();
+ frameOffset = readS.readUint32LE();
+ frameType = readS.readByte();
+ } else {
+ x = readS.readUint16LE();
+ y = readS.readUint16LE();
+ frameOffset = readS.readUint32LE();
+ frameType = readS.readByte();
+ }
+}
+
+void CdtEntry::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(x);
+ writeS.writeUint16LE(y);
+ writeS.writeUint32LE(frameOffset);
+ writeS.writeByte(frameType);
+}
+
+void FrameHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ compSize = readS.readUint32LE();
+ width = readS.readUint16LE();
+ height = readS.readUint16LE();
+
+ if (Sword2Engine::isPsx()) { // In PSX version, frames are half height
+ height *= 2;
+ width = (width % 2) ? width + 1 : width;
+ }
+}
+
+void FrameHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(compSize);
+ writeS.writeUint16LE(width);
+ writeS.writeUint16LE(height);
+}
+
+void MultiScreenHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ palette = readS.readUint32LE();
+ bg_parallax[0] = readS.readUint32LE();
+ bg_parallax[1] = readS.readUint32LE();
+ screen = readS.readUint32LE();
+ fg_parallax[0] = readS.readUint32LE();
+ fg_parallax[1] = readS.readUint32LE();
+ layers = readS.readUint32LE();
+ paletteTable = readS.readUint32LE();
+ maskOffset = readS.readUint32LE();
+}
+
+void MultiScreenHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(palette);
+ writeS.writeUint32LE(bg_parallax[0]);
+ writeS.writeUint32LE(bg_parallax[1]);
+ writeS.writeUint32LE(screen);
+ writeS.writeUint32LE(fg_parallax[0]);
+ writeS.writeUint32LE(fg_parallax[1]);
+ writeS.writeUint32LE(layers);
+ writeS.writeUint32LE(paletteTable);
+ writeS.writeUint32LE(maskOffset);
+}
+
+void ScreenHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ width = readS.readUint16LE();
+ height = readS.readUint16LE();
+ noLayers = readS.readUint16LE();
+}
+
+void ScreenHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(width);
+ writeS.writeUint16LE(height);
+ writeS.writeUint16LE(noLayers);
+}
+
+void LayerHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ x = readS.readUint16LE();
+ y = readS.readUint16LE();
+ width = readS.readUint16LE();
+ height = readS.readUint16LE();
+ maskSize = readS.readUint32LE();
+ offset = readS.readUint32LE();
+}
+
+void LayerHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(x);
+ writeS.writeUint16LE(y);
+ writeS.writeUint16LE(width);
+ writeS.writeUint16LE(height);
+ writeS.writeUint32LE(maskSize);
+ writeS.writeUint32LE(offset);
+}
+
+void TextHeader::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ noOfLines = readS.readUint32LE();
+}
+
+void TextHeader::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(noOfLines);
+}
+
+void PSXScreensEntry::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ bgPlxXres = readS.readUint16LE();
+ bgPlxYres = readS.readUint16LE();
+ bgPlxOffset = readS.readUint32LE();
+ bgPlxSize = readS.readUint32LE();
+ bgXres = readS.readUint16LE();
+ bgYres = readS.readUint16LE();
+ bgOffset = readS.readUint32LE();
+ bgSize = readS.readUint32LE();
+ fgPlxXres = readS.readUint16LE();
+ fgPlxYres = readS.readUint16LE();
+ fgPlxOffset = readS.readUint32LE();
+ fgPlxSize = readS.readUint32LE();
+}
+
+void PSXScreensEntry::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(bgPlxXres);
+ writeS.writeUint16LE(bgPlxYres);
+ writeS.writeUint32LE(bgPlxOffset);
+ writeS.writeUint32LE(bgPlxSize);
+ writeS.writeUint16LE(bgXres);
+ writeS.writeUint16LE(bgYres);
+ writeS.writeUint32LE(bgOffset);
+ writeS.writeUint32LE(bgSize);
+ writeS.writeUint16LE(fgPlxXres);
+ writeS.writeUint16LE(fgPlxYres);
+ writeS.writeUint32LE(fgPlxOffset);
+ writeS.writeUint32LE(fgPlxSize);
+}
+
+void PSXFontEntry::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ offset = readS.readUint16LE() / 2;
+ skipLines = readS.readUint16LE();
+ charWidth = readS.readUint16LE() / 2;
+ charHeight = readS.readUint16LE();
+}
+
+void PSXFontEntry::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(offset);
+ writeS.writeUint16LE(skipLines);
+ writeS.writeUint16LE(charWidth);
+ writeS.writeUint16LE(charHeight);
+}
+
+void Parallax::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ w = readS.readUint16LE();
+ h = readS.readUint16LE();
+}
+
+void Parallax::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(w);
+ writeS.writeUint16LE(h);
+}
+
+void ObjectMouse::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ x1 = readS.readSint32LE();
+ y1 = readS.readSint32LE();
+ x2 = readS.readSint32LE();
+ y2 = readS.readSint32LE();
+ priority = readS.readSint32LE();
+ pointer = readS.readSint32LE();
+}
+
+void ObjectMouse::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeSint32LE(x1);
+ writeS.writeSint32LE(y1);
+ writeS.writeSint32LE(x2);
+ writeS.writeSint32LE(y2);
+ writeS.writeSint32LE(priority);
+ writeS.writeSint32LE(pointer);
+}
+
+void ObjectWalkdata::read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ nWalkFrames = readS.readUint32LE();
+ usingStandingTurnFrames = readS.readUint32LE();
+ usingWalkingTurnFrames = readS.readUint32LE();
+ usingSlowInFrames = readS.readUint32LE();
+ usingSlowOutFrames = readS.readUint32LE();
+
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(nSlowInFrames); i++)
+ nSlowInFrames[i] = readS.readUint32LE();
+
+ for (i = 0; i < ARRAYSIZE(leadingLeg); i++)
+ leadingLeg[i] = readS.readUint32LE();
+
+ for (i = 0; i < ARRAYSIZE(dx); i++)
+ dx[i] = readS.readUint32LE();
+
+ for (i = 0; i < ARRAYSIZE(dy); i++)
+ dy[i] = readS.readUint32LE();
+}
+
+void ObjectWalkdata::write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(nWalkFrames);
+ writeS.writeUint32LE(usingStandingTurnFrames);
+ writeS.writeUint32LE(usingWalkingTurnFrames);
+ writeS.writeUint32LE(usingSlowInFrames);
+ writeS.writeUint32LE(usingSlowOutFrames);
+
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(nSlowInFrames); i++)
+ writeS.writeUint32LE(nSlowInFrames[i]);
+
+ for (i = 0; i < ARRAYSIZE(leadingLeg); i++)
+ writeS.writeUint32LE(leadingLeg[i]);
+
+ for (i = 0; i < ARRAYSIZE(dx); i++)
+ writeS.writeUint32LE(dx[i]);
+
+ for (i = 0; i < ARRAYSIZE(dy); i++)
+ writeS.writeUint32LE(dy[i]);
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/header.h b/engines/sword2/header.h
index 67cbba35af..3f64152bb9 100644
--- a/engines/sword2/header.h
+++ b/engines/sword2/header.h
@@ -28,7 +28,6 @@
#ifndef SWORD2_HEADER_H
#define SWORD2_HEADER_H
-#include "common/stream.h"
#include "common/endian.h"
namespace Sword2 {
@@ -60,25 +59,8 @@ struct ResHeader {
return 44;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- fileType = readS.readByte();
- compType = readS.readByte();
- compSize = readS.readUint32LE();
- decompSize = readS.readUint32LE();
- readS.read(name, NAME_LEN);
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeByte(fileType);
- writeS.writeByte(compType);
- writeS.writeUint32LE(compSize);
- writeS.writeUint32LE(decompSize);
- writeS.write(name, NAME_LEN);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// fileType
@@ -156,45 +138,8 @@ struct AnimHeader {
return 15;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- if (Sword2Engine::isPsx()) {
- noAnimFrames = readS.readUint16LE();
- feetStartX = readS.readUint16LE();
- feetStartY = readS.readUint16LE();
- feetEndX = readS.readUint16LE();
- feetEndY = readS.readUint16LE();
- blend = readS.readUint16LE();
- runTimeComp = readS.readByte();
- feetStartDir = readS.readByte();
- feetEndDir = readS.readByte();
- } else {
- runTimeComp = readS.readByte();
- noAnimFrames = readS.readUint16LE();
- feetStartX = readS.readUint16LE();
- feetStartY = readS.readUint16LE();
- feetStartDir = readS.readByte();
- feetEndX = readS.readUint16LE();
- feetEndY = readS.readUint16LE();
- feetEndDir = readS.readByte();
- blend = readS.readUint16LE();
- }
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeByte(runTimeComp);
- writeS.writeUint16LE(noAnimFrames);
- writeS.writeUint16LE(feetStartX);
- writeS.writeUint16LE(feetStartY);
- writeS.writeByte(feetStartDir);
- writeS.writeUint16LE(feetEndX);
- writeS.writeUint16LE(feetEndY);
- writeS.writeByte(feetEndDir);
- writeS.writeUint16LE(blend);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
@@ -221,38 +166,10 @@ struct CdtEntry {
uint8 frameType; // 0 = print sprite normally with top-left
// corner at (x,y), otherwise see below...
- static int size() {
- if (Sword2Engine::isPsx())
- return 12;
- else
- return 9;
- }
-
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- if (Sword2Engine::isPsx()) {
- readS.readByte(); // Skip a byte in psx version
- x = readS.readUint16LE();
- y = readS.readUint16LE();
- frameOffset = readS.readUint32LE();
- frameType = readS.readByte();
- } else {
- x = readS.readUint16LE();
- y = readS.readUint16LE();
- frameOffset = readS.readUint32LE();
- frameType = readS.readByte();
- }
- }
+ static int size();
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint16LE(x);
- writeS.writeUint16LE(y);
- writeS.writeUint32LE(frameOffset);
- writeS.writeByte(frameType);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// 'frameType' bit values
@@ -277,26 +194,8 @@ struct FrameHeader {
return 8;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- compSize = readS.readUint32LE();
- width = readS.readUint16LE();
- height = readS.readUint16LE();
-
- if (Sword2Engine::isPsx()) { // In PSX version, frames are half height
- height *= 2;
- width = (width % 2) ? width + 1 : width;
- }
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint32LE(compSize);
- writeS.writeUint16LE(width);
- writeS.writeUint16LE(height);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
//----------------------------------------------------------
@@ -331,33 +230,8 @@ struct MultiScreenHeader {
return 36;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- palette = readS.readUint32LE();
- bg_parallax[0] = readS.readUint32LE();
- bg_parallax[1] = readS.readUint32LE();
- screen = readS.readUint32LE();
- fg_parallax[0] = readS.readUint32LE();
- fg_parallax[1] = readS.readUint32LE();
- layers = readS.readUint32LE();
- paletteTable = readS.readUint32LE();
- maskOffset = readS.readUint32LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint32LE(palette);
- writeS.writeUint32LE(bg_parallax[0]);
- writeS.writeUint32LE(bg_parallax[1]);
- writeS.writeUint32LE(screen);
- writeS.writeUint32LE(fg_parallax[0]);
- writeS.writeUint32LE(fg_parallax[1]);
- writeS.writeUint32LE(layers);
- writeS.writeUint32LE(paletteTable);
- writeS.writeUint32LE(maskOffset);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// Screen Header
@@ -371,21 +245,8 @@ struct ScreenHeader {
return 6;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- width = readS.readUint16LE();
- height = readS.readUint16LE();
- noLayers = readS.readUint16LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint16LE(width);
- writeS.writeUint16LE(height);
- writeS.writeUint16LE(noLayers);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// Layer Header
@@ -406,27 +267,8 @@ struct LayerHeader {
return 16;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- x = readS.readUint16LE();
- y = readS.readUint16LE();
- width = readS.readUint16LE();
- height = readS.readUint16LE();
- maskSize = readS.readUint32LE();
- offset = readS.readUint32LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint16LE(x);
- writeS.writeUint16LE(y);
- writeS.writeUint16LE(width);
- writeS.writeUint16LE(height);
- writeS.writeUint32LE(maskSize);
- writeS.writeUint32LE(offset);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
//----------------------------------------------------------
@@ -511,17 +353,8 @@ struct TextHeader {
return 4;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- noOfLines = readS.readUint32LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint32LE(noOfLines);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// a text file has:
@@ -567,39 +400,8 @@ struct PSXScreensEntry {
return 36;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- bgPlxXres = readS.readUint16LE();
- bgPlxYres = readS.readUint16LE();
- bgPlxOffset = readS.readUint32LE();
- bgPlxSize = readS.readUint32LE();
- bgXres = readS.readUint16LE();
- bgYres = readS.readUint16LE();
- bgOffset = readS.readUint32LE();
- bgSize = readS.readUint32LE();
- fgPlxXres = readS.readUint16LE();
- fgPlxYres = readS.readUint16LE();
- fgPlxOffset = readS.readUint32LE();
- fgPlxSize = readS.readUint32LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint16LE(bgPlxXres);
- writeS.writeUint16LE(bgPlxYres);
- writeS.writeUint32LE(bgPlxOffset);
- writeS.writeUint32LE(bgPlxSize);
- writeS.writeUint16LE(bgXres);
- writeS.writeUint16LE(bgYres);
- writeS.writeUint32LE(bgOffset);
- writeS.writeUint32LE(bgSize);
- writeS.writeUint16LE(fgPlxXres);
- writeS.writeUint16LE(fgPlxYres);
- writeS.writeUint32LE(fgPlxOffset);
- writeS.writeUint32LE(fgPlxSize);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// PSXFontEntry is present in font resource file, it is used
@@ -615,23 +417,8 @@ struct PSXFontEntry {
return 8;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- offset = readS.readUint16LE() / 2;
- skipLines = readS.readUint16LE();
- charWidth = readS.readUint16LE() / 2;
- charHeight = readS.readUint16LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint16LE(offset);
- writeS.writeUint16LE(skipLines);
- writeS.writeUint16LE(charWidth);
- writeS.writeUint16LE(charHeight);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
} // End of namespace Sword2
diff --git a/engines/sword2/icons.cpp b/engines/sword2/icons.cpp
index 4652fc0bc1..b115eb373b 100644
--- a/engines/sword2/icons.cpp
+++ b/engines/sword2/icons.cpp
@@ -26,7 +26,7 @@
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/rect.h"
#include "sword2/sword2.h"
diff --git a/engines/sword2/logic.cpp b/engines/sword2/logic.cpp
index 394bf7ddc8..511561c55a 100644
--- a/engines/sword2/logic.cpp
+++ b/engines/sword2/logic.cpp
@@ -278,16 +278,6 @@ void Logic::resetKillList() {
}
/**
- * Pause or unpause the currently playing cutscene movie, if any.
- * @param pause true if pausing, false if unpausing
- */
-
-void Logic::pauseMovie(bool pause) {
- if (_moviePlayer)
- _moviePlayer->pauseMovie(pause);
-}
-
-/**
* Read current location number from script vars
*/
diff --git a/engines/sword2/logic.h b/engines/sword2/logic.h
index 8c49225df2..793f87d037 100644
--- a/engines/sword2/logic.h
+++ b/engines/sword2/logic.h
@@ -317,8 +317,6 @@ public:
void logicOne(uint32 new_script);
void resetKillList();
- void pauseMovie(bool pause);
-
// Read location number from script vars
uint32 getLocationNum();
};
diff --git a/engines/sword2/module.mk b/engines/sword2/module.mk
index c675b9561e..bf586aefff 100644
--- a/engines/sword2/module.mk
+++ b/engines/sword2/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
debug.o \
events.o \
function.o \
+ header.o \
icons.o \
interpreter.o \
layers.o \
diff --git a/engines/sword2/mouse.cpp b/engines/sword2/mouse.cpp
index 4dbb38eaca..7998079944 100644
--- a/engines/sword2/mouse.cpp
+++ b/engines/sword2/mouse.cpp
@@ -28,6 +28,7 @@
#include "common/system.h"
#include "common/events.h"
+#include "common/memstream.h"
#include "graphics/cursorman.h"
diff --git a/engines/sword2/music.cpp b/engines/sword2/music.cpp
index c052aa6b46..1519c5d73f 100644
--- a/engines/sword2/music.cpp
+++ b/engines/sword2/music.cpp
@@ -33,6 +33,8 @@
#include "common/file.h"
+#include "common/memstream.h"
+#include "common/substream.h"
#include "common/system.h"
#include "sound/decoders/mp3.h"
@@ -496,9 +498,16 @@ int Sound::readBuffer(int16 *buffer, const int numSamples) {
memset(buffer, 0, 2 * numSamples);
if (!_mixBuffer || numSamples > _mixBufferLen) {
- if (_mixBuffer)
- _mixBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
- else
+ if (_mixBuffer) {
+ int16 *newBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
+ if (newBuffer) {
+ _mixBuffer = newBuffer;
+ } else {
+ // We can't use the old buffer any more. It's too small.
+ free(_mixBuffer);
+ _mixBuffer = 0;
+ }
+ } else
_mixBuffer = (int16 *)malloc(2 * numSamples);
_mixBufferLen = numSamples;
diff --git a/engines/sword2/object.h b/engines/sword2/object.h
index b6b6ca5174..53819c08cc 100644
--- a/engines/sword2/object.h
+++ b/engines/sword2/object.h
@@ -28,7 +28,6 @@
#ifndef SWORD2_OBJECT_H
#define SWORD2_OBJECT_H
-#include "common/stream.h"
#include "common/endian.h"
namespace Sword2 {
@@ -52,27 +51,8 @@ struct ObjectMouse {
return 24;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- x1 = readS.readSint32LE();
- y1 = readS.readSint32LE();
- x2 = readS.readSint32LE();
- y2 = readS.readSint32LE();
- priority = readS.readSint32LE();
- pointer = readS.readSint32LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeSint32LE(x1);
- writeS.writeSint32LE(y1);
- writeS.writeSint32LE(x2);
- writeS.writeSint32LE(y2);
- writeS.writeSint32LE(priority);
- writeS.writeSint32LE(pointer);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
// logic structure - contains fields used in logic script processing
@@ -295,53 +275,8 @@ struct ObjectWalkdata {
return 916;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- nWalkFrames = readS.readUint32LE();
- usingStandingTurnFrames = readS.readUint32LE();
- usingWalkingTurnFrames = readS.readUint32LE();
- usingSlowInFrames = readS.readUint32LE();
- usingSlowOutFrames = readS.readUint32LE();
-
- int i;
-
- for (i = 0; i < ARRAYSIZE(nSlowInFrames); i++)
- nSlowInFrames[i] = readS.readUint32LE();
-
- for (i = 0; i < ARRAYSIZE(leadingLeg); i++)
- leadingLeg[i] = readS.readUint32LE();
-
- for (i = 0; i < ARRAYSIZE(dx); i++)
- dx[i] = readS.readUint32LE();
-
- for (i = 0; i < ARRAYSIZE(dy); i++)
- dy[i] = readS.readUint32LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint32LE(nWalkFrames);
- writeS.writeUint32LE(usingStandingTurnFrames);
- writeS.writeUint32LE(usingWalkingTurnFrames);
- writeS.writeUint32LE(usingSlowInFrames);
- writeS.writeUint32LE(usingSlowOutFrames);
-
- int i;
-
- for (i = 0; i < ARRAYSIZE(nSlowInFrames); i++)
- writeS.writeUint32LE(nSlowInFrames[i]);
-
- for (i = 0; i < ARRAYSIZE(leadingLeg); i++)
- writeS.writeUint32LE(leadingLeg[i]);
-
- for (i = 0; i < ARRAYSIZE(dx); i++)
- writeS.writeUint32LE(dx[i]);
-
- for (i = 0; i < ARRAYSIZE(dy); i++)
- writeS.writeUint32LE(dy[i]);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
} // End of namespace Sword2
diff --git a/engines/sword2/palette.cpp b/engines/sword2/palette.cpp
index 84ebd142ca..dd1a2ba877 100644
--- a/engines/sword2/palette.cpp
+++ b/engines/sword2/palette.cpp
@@ -171,6 +171,9 @@ void Screen::setPalette(int16 startEntry, int16 noEntries, byte *colourTable, ui
}
void Screen::dimPalette(bool dim) {
+ if (getFadeStatus() != RDFADE_NONE)
+ return;
+
if (dim != _dimPalette) {
_dimPalette = dim;
setSystemPalette(_palette, 0, 256);
diff --git a/engines/sword2/protocol.cpp b/engines/sword2/protocol.cpp
index 03b021f04c..6c47c07401 100644
--- a/engines/sword2/protocol.cpp
+++ b/engines/sword2/protocol.cpp
@@ -412,8 +412,8 @@ byte *Sword2Engine::fetchPsxParallax(uint32 location, uint8 level) {
debug(2, "fetchPsxParallax() -> %s parallax, xRes: %u, yRes: %u", (level == 0) ? "Background" : "Foreground", plxXres, plxYres);
// Calculate the number of tiles which compose the parallax grid.
- horTiles = plxXres % 64 ? (plxXres / 64) + 1 : plxXres / 64;
- verTiles = plxYres % 16 ? (plxYres / 16) + 1 : plxYres / 16;
+ horTiles = (plxXres % 64) ? (plxXres / 64) + 1 : plxXres / 64;
+ verTiles = (plxYres % 16) ? (plxYres / 16) + 1 : plxYres / 16;
totSize = plxSize + horTiles * verTiles * 4 + 8;
diff --git a/engines/sword2/render.cpp b/engines/sword2/render.cpp
index 1a8dcdab16..99295be571 100644
--- a/engines/sword2/render.cpp
+++ b/engines/sword2/render.cpp
@@ -731,8 +731,8 @@ int32 Screen::initialisePsxParallaxLayer(byte *parallax) {
data = parallax + xTiles * yTiles * 4;
_xBlocks[_layer] = xTiles;
- _yBlocks[_layer] = (yTiles / 2) + (yTiles % 2 ? 1 : 0);
- bool oddTiles = (yTiles % 2 ? true : false);
+ _yBlocks[_layer] = (yTiles / 2) + ((yTiles % 2) ? 1 : 0);
+ bool oddTiles = ((yTiles % 2) ? true : false);
_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
if (!_blockSurfaces[_layer])
diff --git a/engines/sword2/resman.cpp b/engines/sword2/resman.cpp
index 81d74f355b..fa9c396ef3 100644
--- a/engines/sword2/resman.cpp
+++ b/engines/sword2/resman.cpp
@@ -302,6 +302,8 @@ byte *ResourceManager::openResource(uint32 res, bool dump) {
readCluIndex(cluFileNum, file);
}
+ assert(_resFiles[cluFileNum].entryTab);
+
uint32 pos = _resFiles[cluFileNum].entryTab[actual_res * 2 + 0];
uint32 len = _resFiles[cluFileNum].entryTab[actual_res * 2 + 1];
@@ -474,15 +476,18 @@ void ResourceManager::readCluIndex(uint16 fileNum, Common::File *file) {
file->seek(table_offset);
assert((tableSize % 8) == 0);
- _resFiles[fileNum].entryTab = (uint32*)malloc(tableSize);
+ _resFiles[fileNum].entryTab = (uint32 *)malloc(tableSize);
_resFiles[fileNum].numEntries = tableSize / 8;
+
+ assert(_resFiles[fileNum].entryTab);
+
file->read(_resFiles[fileNum].entryTab, tableSize);
if (file->eos() || file->err())
error("unable to read index table from file %s", _resFiles[fileNum].fileName);
#ifdef SCUMM_BIG_ENDIAN
for (int tabCnt = 0; tabCnt < _resFiles[fileNum].numEntries * 2; tabCnt++)
- _resFiles[fileNum].entryTab[tabCnt] = FROM_LE_32(_resFiles[fileNum].entryTab[tabCnt]);
+ _resFiles[fileNum].entryTab[tabCnt] = FROM_LE_32(_resFiles[fileNum].entryTab[tabCnt]);
#endif
}
diff --git a/engines/sword2/router.cpp b/engines/sword2/router.cpp
index 9c79f1f9cb..1548536ccb 100644
--- a/engines/sword2/router.cpp
+++ b/engines/sword2/router.cpp
@@ -26,7 +26,7 @@
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "sword2/sword2.h"
#include "sword2/defs.h"
diff --git a/engines/sword2/saveload.cpp b/engines/sword2/saveload.cpp
index 9258d37fe3..fab360a8ac 100644
--- a/engines/sword2/saveload.cpp
+++ b/engines/sword2/saveload.cpp
@@ -35,6 +35,7 @@
// ---------------------------------------------------------------------------
+#include "common/memstream.h"
#include "common/savefile.h"
#include "sword2/sword2.h"
diff --git a/engines/sword2/screen.h b/engines/sword2/screen.h
index afad64011d..0abaebc5af 100644
--- a/engines/sword2/screen.h
+++ b/engines/sword2/screen.h
@@ -192,19 +192,8 @@ struct Parallax {
return 4;
}
- void read(byte *addr) {
- Common::MemoryReadStream readS(addr, size());
-
- w = readS.readUint16LE();
- h = readS.readUint16LE();
- }
-
- void write(byte *addr) {
- Common::MemoryWriteStream writeS(addr, size());
-
- writeS.writeUint16LE(w);
- writeS.writeUint16LE(h);
- }
+ void read(byte *addr);
+ void write(byte *addr);
};
class Screen {
diff --git a/engines/sword2/sound.cpp b/engines/sword2/sound.cpp
index e36c946eba..abacbd16f0 100644
--- a/engines/sword2/sound.cpp
+++ b/engines/sword2/sound.cpp
@@ -37,6 +37,7 @@
#include "common/file.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "sword2/sword2.h"
@@ -393,18 +394,6 @@ int32 Sound::stopFx(int32 i) {
return RD_OK;
}
-void Sound::pauseAllSound() {
- pauseMusic();
- pauseSpeech();
- pauseFx();
-}
-
-void Sound::unpauseAllSound() {
- unpauseMusic();
- unpauseSpeech();
- unpauseFx();
-}
-
void Sound::printFxQueue() {
int freeSlots = 0;
diff --git a/engines/sword2/sound.h b/engines/sword2/sound.h
index 29bbdf22ca..5acc39179f 100644
--- a/engines/sword2/sound.h
+++ b/engines/sword2/sound.h
@@ -262,9 +262,6 @@ public:
void pauseMusic();
void unpauseMusic();
- void pauseAllSound();
- void unpauseAllSound();
-
void playMovieSound(int32 res, int type);
void stopMovieSounds();
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index b3b688771a..9c67fcdf25 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -290,13 +290,6 @@ Sword2Engine::Sword2Engine(OSystem *syst) : Engine(syst) {
_wantSfxDebug = false;
-#ifdef SWORD2_DEBUG
- _stepOneCycle = false;
- _renderSkip = false;
-#endif
-
- _gamePaused = false;
-
_gameCycle = 0;
_gameSpeed = 1;
@@ -481,13 +474,6 @@ Common::Error Sword2Engine::run() {
while (1) {
_debugger->onFrame();
-#ifdef SWORD2_DEBUG
- if (_stepOneCycle) {
- pauseEngine(true);
- _stepOneCycle = false;
- }
-#endif
-
// Handle GMM Loading
if (_gmmLoadSlot != -1) {
@@ -516,10 +502,13 @@ Common::Error Sword2Engine::run() {
} else if (ke->kbd.hasFlags(0) || ke->kbd.hasFlags(Common::KBD_SHIFT)) {
switch (ke->kbd.keycode) {
case Common::KEYCODE_p:
- if (_gamePaused)
+ if (isPaused()) {
+ _screen->dimPalette(false);
pauseEngine(false);
- else
+ } else {
pauseEngine(true);
+ _screen->dimPalette(true);
+ }
break;
#if 0
// Disabled because of strange rumors about the
@@ -533,17 +522,6 @@ Common::Error Sword2Engine::run() {
}
break;
#endif
-#ifdef SWORD2_DEBUG
- case Common::KEYCODE_SPACE:
- if (_gamePaused) {
- _stepOneCycle = true;
- pauseEngine(false);
- }
- break;
- case Common::KEYCODE_s:
- _renderSkip = !_renderSkip;
- break;
-#endif
default:
break;
}
@@ -551,7 +529,7 @@ Common::Error Sword2Engine::run() {
}
// skip GameCycle if we're paused
- if (!_gamePaused) {
+ if (!isPaused()) {
_gameCycle++;
gameCycle();
}
@@ -566,15 +544,7 @@ Common::Error Sword2Engine::run() {
// creates the debug text blocks
_debugger->buildDebugText();
-#ifdef SWORD2_DEBUG
- // if not in console & '_renderSkip' is set, only render
- // display once every 4 game-cycles
-
- if (!_renderSkip || (_gameCycle % 4) == 0)
- _screen->buildDisplay();
-#else
_screen->buildDisplay();
-#endif
}
return Common::kNoError;
@@ -798,49 +768,13 @@ void Sword2Engine::sleepUntil(uint32 time) {
}
}
-void Sword2Engine::pauseEngine(bool pause) {
- if (pause == _gamePaused)
- return;
-
- // We don't need to hide the cursor for outside pausing. Not as long
- // as it replaces the cursor with the GUI cursor, at least.
-
- _mouse->pauseEngine(pause);
- pauseEngineIntern(pause);
-
- if (pause) {
-#ifdef SWORD2_DEBUG
- // Don't dim it if we're single-stepping through frames
- // dim the palette during the pause
-
- if (!_stepOneCycle)
- _screen->dimPalette(true);
-#else
- _screen->dimPalette(true);
-#endif
- } else {
- _screen->dimPalette(false);
-
- // If mouse is about or we're in a chooser menu
- if (!_mouse->getMouseStatus() || _mouse->isChoosing())
- _mouse->setMouse(NORMAL_MOUSE_ID);
- }
-}
-
void Sword2Engine::pauseEngineIntern(bool pause) {
- if (pause == _gamePaused)
- return;
+ Engine::pauseEngineIntern(pause);
if (pause) {
- _sound->pauseAllSound();
- _logic->pauseMovie(true);
_screen->pauseScreen(true);
- _gamePaused = true;
} else {
- _logic->pauseMovie(false);
_screen->pauseScreen(false);
- _sound->unpauseAllSound();
- _gamePaused = false;
}
}
diff --git a/engines/sword2/sword2.h b/engines/sword2/sword2.h
index 201ce5e836..302627b635 100644
--- a/engines/sword2/sword2.h
+++ b/engines/sword2/sword2.h
@@ -53,8 +53,8 @@ class OSystem;
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Broken Sword II: The Smoking Mirror
*/
namespace Sword2 {
@@ -155,8 +155,6 @@ public:
Sword2Engine(OSystem *syst);
~Sword2Engine();
- void pauseEngine(bool pause);
-
int getFramesPerSecond();
void registerDefaultSettings();
@@ -204,11 +202,6 @@ public:
int32 _gameCycle;
-#ifdef SWORD2_DEBUG
- bool _renderSkip;
- bool _stepOneCycle;
-#endif
-
#if RIGHT_CLICK_CLEARS_LUGGAGE
bool heldIsInInventory();
#endif
@@ -237,8 +230,6 @@ public:
char *getSaveFileName(uint16 slotNo);
uint32 findBufferSize();
- bool _gamePaused;
-
void startGame();
void gameCycle();
void restartGame();
diff --git a/engines/sword25/console.cpp b/engines/sword25/console.cpp
new file mode 100644
index 0000000000..014c748182
--- /dev/null
+++ b/engines/sword25/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sword25/console.h"
+#include "sword25/sword25.h"
+
+namespace Sword25 {
+
+Sword25Console::Sword25Console(Sword25Engine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+Sword25Console::~Sword25Console() {
+}
+
+void Sword25Console::preEnter() {
+}
+
+void Sword25Console::postEnter() {
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/imageloader_ids.h b/engines/sword25/console.h
index 8fb6872ad7..dd053c19f6 100644
--- a/engines/sword25/gfx/image/imageloader_ids.h
+++ b/engines/sword25/console.h
@@ -23,40 +23,28 @@
*
*/
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
+#ifndef SWORD25_CONSOLE_H
+#define SWORD25_CONSOLE_H
-/*
- imageloader_ids.h
- -----------------
- In dieser Datei sind alle ImageLoader verzeichnet.
- JEDER neuer ImageLoader muss hier eingetragen werden, ansonsten wird er beim Laden eines Bildes nicht berücksichtigt.
+#include "gui/debugger.h"
- Autor: Malte Thiesen
-*/
+namespace Sword25 {
-#include "sword25/gfx/image/imageloader.h"
+class Sword25Engine;
-// Die Headerdateien der ImageLoader müssen hier eingebunden werden
-#include "sword25/gfx/image/pngloader.h"
-#include "sword25/gfx/image/b25sloader.h"
+class Sword25Console : public GUI::Debugger {
+public:
+ Sword25Console(Sword25Engine *vm);
+ virtual ~Sword25Console(void);
-namespace Sword25 {
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
-// Die Tabelle enthält Pointer auf statische Member-Funktionen innerhalb der Klassen, die eine Instanz der Klasse
-// erzeugen
-typedef ImageLoader*(*BS_IMAGELOADER_NEW)();
-const BS_IMAGELOADER_NEW BS_IMAGELOADER_IDS[] = {
- PNGLoader::CreateInstance,
- B25SLoader::CreateInstance,
+private:
+ Sword25Engine *_vm;
};
-const int BS_IMAGELOADER_COUNT = sizeof(BS_IMAGELOADER_IDS) / sizeof(BS_IMAGELOADER_NEW);
-
} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp
index e210f4d27c..3900df2fcf 100644
--- a/engines/sword25/detection.cpp
+++ b/engines/sword25/detection.cpp
@@ -24,10 +24,12 @@
*/
#include "base/plugins.h"
-
+#include "common/savefile.h"
+#include "common/system.h"
#include "engines/advancedDetector.h"
#include "sword25/sword25.h"
+#include "sword25/kernel/persistenceservice.h"
namespace Sword25 {
uint32 Sword25Engine::getGameFlags() const { return _gameDescription->flags; }
@@ -68,7 +70,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_TABLE_END_MARKER
};
-} // end of namespace Sword25
+} // End of namespace Sword25
static const char *directoryGlobs[] = {
"system", // Used by extracted dats
@@ -113,6 +115,9 @@ public:
}
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); }
+ virtual SaveStateList listSaves(const char *target) const;
};
bool Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
@@ -122,6 +127,31 @@ bool Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADG
return desc != 0;
}
+bool Sword25MetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves);
+}
+
+SaveStateList Sword25MetaEngine::listSaves(const char *target) const {
+ Common::String pattern = target;
+ pattern = pattern + ".???";
+ SaveStateList saveList;
+
+ Sword25::PersistenceService ps;
+ Sword25::setGameTarget(target);
+
+ ps.reloadSlots();
+
+ for (uint i = 0; i < ps.getSlotCount(); ++i) {
+ if (ps.isSlotOccupied(i)) {
+ Common::String desc = ps.getSavegameDescription(i);
+ saveList.push_back(SaveStateDescriptor(i, desc));
+ }
+ }
+
+ return saveList;
+}
+
#if PLUGIN_ENABLED_DYNAMIC(SWORD25)
REGISTER_PLUGIN_DYNAMIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
#else
diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp
index f6757b9a8e..4193e02b2e 100644
--- a/engines/sword25/fmv/movieplayer.cpp
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -39,16 +39,15 @@
#include "sword25/package/packagemanager.h"
#include "sword25/sfx/soundengine.h"
+#define INDIRECTRENDERING 1
+
namespace Sword25 {
#define BS_LOG_PREFIX "MOVIEPLAYER"
#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */
-Service *OggTheora_CreateObject(Kernel *pKernel) {
- return new MoviePlayer(pKernel);
-}
-
+#ifdef USE_THEORADEC
MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel), _decoder(g_system->getMixer()) {
if (!registerScriptBindings())
BS_LOG_ERRORLN("Script bindings could not be registered.");
@@ -62,20 +61,22 @@ MoviePlayer::~MoviePlayer() {
bool MoviePlayer::loadMovie(const Common::String &filename, uint z) {
// Get the file and load it into the decoder
- Common::SeekableReadStream *in = Kernel::GetInstance()->GetPackage()->getStream(filename);
+ Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename);
_decoder.load(in);
// Ausgabebitmap erstellen
- GraphicEngine *pGfx = Kernel::GetInstance()->GetGfx();
- _outputBitmap = pGfx->GetMainPanel()->addDynamicBitmap(_decoder.getWidth(), _decoder.getHeight());
+ GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
+
+#if INDIRECTRENDERING
+ _outputBitmap = pGfx->getMainPanel()->addDynamicBitmap(_decoder.getWidth(), _decoder.getHeight());
if (!_outputBitmap.isValid()) {
BS_LOG_ERRORLN("Output bitmap for movie playback could not be created.");
return false;
}
// Skalierung des Ausgabebitmaps berechnen, so dass es möglichst viel Bildschirmfläche einnimmt.
- float screenToVideoWidth = (float)pGfx->GetDisplayWidth() / (float)_outputBitmap->getWidth();
- float screenToVideoHeight = (float)pGfx->GetDisplayHeight() / (float)_outputBitmap->getHeight();
+ float screenToVideoWidth = (float)pGfx->getDisplayWidth() / (float)_outputBitmap->getWidth();
+ float screenToVideoHeight = (float)pGfx->getDisplayHeight() / (float)_outputBitmap->getHeight();
float scaleFactor = MIN(screenToVideoWidth, screenToVideoHeight);
if (abs((int)(scaleFactor - 1.0f)) < FLT_EPSILON)
@@ -87,8 +88,19 @@ bool MoviePlayer::loadMovie(const Common::String &filename, uint z) {
_outputBitmap->setZ(z);
// Ausgabebitmap auf dem Bildschirm zentrieren
- _outputBitmap->setX((pGfx->GetDisplayWidth() - _outputBitmap->getWidth()) / 2);
- _outputBitmap->setY((pGfx->GetDisplayHeight() - _outputBitmap->getHeight()) / 2);
+ _outputBitmap->setX((pGfx->getDisplayWidth() - _outputBitmap->getWidth()) / 2);
+ _outputBitmap->setY((pGfx->getDisplayHeight() - _outputBitmap->getHeight()) / 2);
+#else
+ _backSurface = pGfx->getSurface();
+
+ _outX = (pGfx->getDisplayWidth() - _decoder.getWidth()) / 2;
+ _outY = (pGfx->getDisplayHeight() - _decoder.getHeight()) / 2;
+
+ if (_outX < 0)
+ _outX = 0;
+ if (_outY < 0)
+ _outY = 0;
+#endif
return true;
}
@@ -113,11 +125,21 @@ bool MoviePlayer::pause() {
void MoviePlayer::update() {
if (_decoder.isVideoLoaded()) {
Graphics::Surface *s = _decoder.decodeNextFrame();
-
- // Transfer the next frame
- assert(s->bytesPerPixel == 4);
- byte *frameData = (byte *)s->getBasePtr(0, 0);
- _outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
+ if (s) {
+ // Transfer the next frame
+ assert(s->bytesPerPixel == 4);
+
+#if INDIRECTRENDERING
+ byte *frameData = (byte *)s->getBasePtr(0, 0);
+ _outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
+#else
+ g_system->copyRectToScreen((byte *)s->getBasePtr(0, 0), s->pitch, _outX, _outY, MIN(s->w, _backSurface->w), MIN(s->h, _backSurface->h));
+ g_system->updateScreen();
+#endif
+ } else {
+ // Movie complete, so unload the movie
+ unloadMovie();
+ }
}
}
@@ -141,9 +163,9 @@ void MoviePlayer::setScaleFactor(float scaleFactor) {
_outputBitmap->setScaleFactor(scaleFactor);
// Ausgabebitmap auf dem Bildschirm zentrieren
- GraphicEngine *gfxPtr = Kernel::GetInstance()->GetGfx();
- _outputBitmap->setX((gfxPtr->GetDisplayWidth() - _outputBitmap->getWidth()) / 2);
- _outputBitmap->setY((gfxPtr->GetDisplayHeight() - _outputBitmap->getHeight()) / 2);
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
+ _outputBitmap->setX((gfxPtr->getDisplayWidth() - _outputBitmap->getWidth()) / 2);
+ _outputBitmap->setY((gfxPtr->getDisplayHeight() - _outputBitmap->getHeight()) / 2);
}
}
@@ -151,4 +173,56 @@ double MoviePlayer::getTime() {
return _decoder.getElapsedTime() / 1000.0;
}
+#else // USE_THEORADEC
+
+MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel) {
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+MoviePlayer::~MoviePlayer() {
+}
+
+bool MoviePlayer::loadMovie(const Common::String &Filename, unsigned int Z) {
+ return true;
+}
+
+bool MoviePlayer::unloadMovie() {
+ return true;
+}
+
+bool MoviePlayer::play() {
+ return true;
+}
+
+bool MoviePlayer::pause() {
+ return true;
+}
+
+void MoviePlayer::update() {
+}
+
+bool MoviePlayer::isMovieLoaded() {
+ return true;
+}
+
+bool MoviePlayer::isPaused() {
+ return true;
+}
+
+float MoviePlayer::getScaleFactor() {
+ return 1.0f;
+}
+
+void MoviePlayer::setScaleFactor(float ScaleFactor) {
+}
+
+double MoviePlayer::getTime() {
+ return 1.0;
+}
+
+#endif // USE_THEORADEC
+
} // End of namespace Sword25
diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h
index 96beb648c0..350407cea5 100644
--- a/engines/sword25/fmv/movieplayer.h
+++ b/engines/sword25/fmv/movieplayer.h
@@ -35,11 +35,16 @@
#ifndef SWORD25_MOVIEPLAYER_H
#define SWORD25_MOVIEPLAYER_H
+#include "common/scummsys.h" // for USE_THEORADEC
+
#include "sword25/kernel/common.h"
#include "sword25/kernel/service.h"
-#include "sword25/fmv/theora_decoder.h"
#include "sword25/gfx/bitmap.h"
+#ifdef USE_THEORADEC
+#include "sword25/fmv/theora_decoder.h"
+#endif
+
namespace Sword25 {
class MoviePlayer : public Service {
@@ -135,9 +140,15 @@ public:
private:
bool registerScriptBindings();
+
+#ifdef USE_THEORADEC
TheoraDecoder _decoder;
+ Graphics::Surface *_backSurface;
+ int _outX, _outY;
+
RenderObjectPtr<Bitmap> _outputBitmap;
+#endif
};
} // End of namespace Sword25
diff --git a/engines/sword25/fmv/movieplayer_script.cpp b/engines/sword25/fmv/movieplayer_script.cpp
index 13bb149672..aa854448ff 100644
--- a/engines/sword25/fmv/movieplayer_script.cpp
+++ b/engines/sword25/fmv/movieplayer_script.cpp
@@ -32,6 +32,8 @@
*
*/
+#include "common/scummsys.h" // for USE_THEORADEC
+
#include "sword25/kernel/common.h"
#include "sword25/kernel/kernel.h"
#include "sword25/script/script.h"
@@ -42,7 +44,7 @@
namespace Sword25 {
int loadMovie(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushbooleancpp(L, FMVPtr->loadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<uint>(luaL_checknumber(L, 2)) : 10));
@@ -51,7 +53,7 @@ int loadMovie(lua_State *L) {
}
int unloadMovie(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushbooleancpp(L, FMVPtr->unloadMovie());
@@ -60,7 +62,7 @@ int unloadMovie(lua_State *L) {
}
int play(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushbooleancpp(L, FMVPtr->play());
@@ -69,7 +71,7 @@ int play(lua_State *L) {
}
int pause(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushbooleancpp(L, FMVPtr->pause());
@@ -78,7 +80,7 @@ int pause(lua_State *L) {
}
int update(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
FMVPtr->update();
@@ -87,7 +89,7 @@ int update(lua_State *L) {
}
int isMovieLoaded(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushbooleancpp(L, FMVPtr->isMovieLoaded());
@@ -96,7 +98,7 @@ int isMovieLoaded(lua_State *L) {
}
int isPaused(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushbooleancpp(L, FMVPtr->isPaused());
@@ -105,7 +107,7 @@ int isPaused(lua_State *L) {
}
int getScaleFactor(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushnumber(L, FMVPtr->getScaleFactor());
@@ -114,7 +116,7 @@ int getScaleFactor(lua_State *L) {
}
int setScaleFactor(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
FMVPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 1)));
@@ -123,7 +125,7 @@ int setScaleFactor(lua_State *L) {
}
int getTime(lua_State *L) {
- MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
BS_ASSERT(FMVPtr);
lua_pushnumber(L, FMVPtr->getTime());
@@ -148,9 +150,7 @@ const luaL_reg LIBRARY_FUNCTIONS[] = {
};
bool MoviePlayer::registerScriptBindings() {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pScript = Kernel::getInstance()->getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
index 9b1951828e..d6c2544fe5 100644
--- a/engines/sword25/fmv/theora_decoder.cpp
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -37,6 +37,8 @@
*/
#include "sword25/fmv/theora_decoder.h"
+
+#ifdef USE_THEORADEC
#include "sword25/fmv/yuvtorgba.h"
#include "common/system.h"
#include "sound/decoders/raw.h"
@@ -242,10 +244,12 @@ bool TheoraDecoder::load(Common::SeekableReadStream *stream) {
if (_theoraComment.user_comments[i]) {
int len = _theoraComment.comment_lengths[i];
char *value = (char *)malloc(len + 1);
- memcpy(value, _theoraComment.user_comments[i], len);
- value[len] = '\0';
- debug(1, "\t%s", value);
- free(value);
+ if (value) {
+ memcpy(value, _theoraComment.user_comments[i], len);
+ value[len] = '\0';
+ debug(1, "\t%s", value);
+ free(value);
+ }
}
}
}
@@ -343,7 +347,7 @@ Graphics::Surface *TheoraDecoder::decodeNextFrame() {
int maxsamples = (AUDIOFD_FRAGSIZE - _audiobufFill) / 2 / _vorbisInfo.channels;
for (i = 0; i < ret && i < maxsamples; i++)
for (j = 0; j < _vorbisInfo.channels; j++) {
- int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32768);
+ int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767);
_audiobuf[count++] = val;
}
@@ -397,8 +401,7 @@ Graphics::Surface *TheoraDecoder::decodeNextFrame() {
}
if (!_videobufReady && !_audiobufReady && _fileStream->eos()) {
- close();
- return _surface;
+ return NULL;
}
if (!_videobufReady || !_audiobufReady) {
@@ -410,11 +413,15 @@ Graphics::Surface *TheoraDecoder::decodeNextFrame() {
}
// If playback has begun, top audio buffer off immediately.
-/* FIXME: This is currently crashing
- if (_stateFlag) {
+ if (_stateFlag && _audiobufReady) {
_audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO);
+
+ // The audio mixer is now responsible for the old audio buffer.
+ // We need to create a new one.
+ _audiobuf = (ogg_int16_t *)calloc(AUDIOFD_FRAGSIZE, sizeof(ogg_int16_t));
+ _audiobufFill = 0;
+ _audiobufReady = false;
}
-*/
// are we at or past time for this video frame?
if (_stateFlag && _videobufReady) {
@@ -490,3 +497,5 @@ Audio::QueuingAudioStream *TheoraDecoder::createAudioStream() {
}
} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h
index 12d8035c0a..f6c622563b 100644
--- a/engines/sword25/fmv/theora_decoder.h
+++ b/engines/sword25/fmv/theora_decoder.h
@@ -26,6 +26,10 @@
#ifndef SWORD25_THEORADECODER_H
#define SWORD25_THEORADECODER_H
+#include "common/scummsys.h" // for USE_THEORADEC
+
+#ifdef USE_THEORADEC
+
#include "graphics/video/video_decoder.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
@@ -150,3 +154,5 @@ private:
} // End of namespace Sword25
#endif
+
+#endif
diff --git a/engines/sword25/fmv/yuvtorgba.cpp b/engines/sword25/fmv/yuvtorgba.cpp
index 12d11b6784..e9d0189265 100644
--- a/engines/sword25/fmv/yuvtorgba.cpp
+++ b/engines/sword25/fmv/yuvtorgba.cpp
@@ -34,6 +34,8 @@
#include "sword25/fmv/yuvtorgba.h"
+#ifdef USE_THEORADEC
+
namespace Sword25 {
static const int PRECISION = 32768;
@@ -241,3 +243,5 @@ void YUVtoBGRA::translate(th_ycbcr_buffer &YUVBuffer, const th_info &theoraInfo,
}
} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/fmv/yuvtorgba.h b/engines/sword25/fmv/yuvtorgba.h
index 15fa85b7e4..675248e6e0 100644
--- a/engines/sword25/fmv/yuvtorgba.h
+++ b/engines/sword25/fmv/yuvtorgba.h
@@ -35,6 +35,9 @@
#ifndef SWORD25_YUVTORGBA_H
#define SWORD25_YUVTORGBA_H
+#include "common/scummsys.h" // for USE_THEORADEC
+
+#ifdef USE_THEORADEC
#include "sword25/kernel/common.h"
#include <theora/theora.h>
#include <theora/codec.h>
@@ -49,3 +52,5 @@ public:
} // End of namespace Sword25
#endif
+
+#endif
diff --git a/engines/sword25/gfx/animation.cpp b/engines/sword25/gfx/animation.cpp
index 7ec445acb8..0d3baae347 100644
--- a/engines/sword25/gfx/animation.cpp
+++ b/engines/sword25/gfx/animation.cpp
@@ -38,7 +38,6 @@
#include "sword25/kernel/resmanager.h"
#include "sword25/kernel/inputpersistenceblock.h"
#include "sword25/kernel/outputpersistenceblock.h"
-#include "sword25/kernel/callbackregistry.h"
#include "sword25/package/packagemanager.h"
#include "sword25/gfx/image/image.h"
#include "sword25/gfx/animationtemplate.h"
@@ -99,8 +98,8 @@ Animation::Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject
void Animation::initializeAnimationResource(const Common::String &fileName) {
// Die Resource wird für die gesamte Lebensdauer des Animations-Objektes gelockt.
- Resource *resourcePtr = Kernel::GetInstance()->GetResourceManager()->RequestResource(fileName);
- if (resourcePtr && resourcePtr->GetType() == Resource::TYPE_ANIMATION)
+ Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(fileName);
+ if (resourcePtr && resourcePtr->getType() == Resource::TYPE_ANIMATION)
_animationResourcePtr = static_cast<AnimationResource *>(resourcePtr);
else {
BS_LOG_ERRORLN("The resource \"%s\" could not be requested. The Animation can't be created.", fileName.c_str());
@@ -133,14 +132,14 @@ Animation::~Animation() {
getAnimationDescription()->unlock();
}
- // Delete Callbacks
- Common::Array<ANIMATION_CALLBACK_DATA>::iterator it = _deleteCallbacks.begin();
- for (; it != _deleteCallbacks.end(); it++)((*it).Callback)((*it).Data);
+ // Invoke the "delete" callback
+ if (_deleteCallback)
+ (_deleteCallback)(getHandle());
}
void Animation::play() {
- // Wenn die Animation zuvor komplett durchgelaufen ist, wird sie wieder von Anfang abgespielt
+ // If the animation was completed, then play it again from the start.
if (_finished)
stop();
@@ -182,13 +181,13 @@ bool Animation::doRender() {
BS_ASSERT(_currentFrame < animationDescriptionPtr->getFrameCount());
// Bitmap des aktuellen Frames holen
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->getFrame(_currentFrame).fileName);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(_currentFrame).fileName);
BS_ASSERT(pResource);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
// Framebufferobjekt holen
- GraphicEngine *pGfx = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
BS_ASSERT(pGfx);
// Bitmap zeichnen
@@ -242,28 +241,20 @@ void Animation::frameNotification(int timeElapsed) {
BS_ASSERT(0);
}
- // Überläufe behandeln
+ // Deal with overflows
if (tmpCurFrame < 0) {
- // Loop-Point Callbacks
- for (uint i = 0; i < _loopPointCallbacks.size();) {
- if ((_loopPointCallbacks[i].Callback)(_loopPointCallbacks[i].Data) == false) {
- _loopPointCallbacks.remove_at(i);
- } else
- i++;
- }
+ // Loop-Point callback
+ if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
+ _loopPointCallback = 0;
- // Ein Unterlauf darf nur auftreten, wenn der Animationstyp JOJO ist.
+ // An underflow may only occur if the animation type is JOJO.
BS_ASSERT(animationDescriptionPtr->getAnimationType() == AT_JOJO);
tmpCurFrame = - tmpCurFrame;
_direction = FORWARD;
} else if (static_cast<uint>(tmpCurFrame) >= animationDescriptionPtr->getFrameCount()) {
- // Loop-Point Callbacks
- for (uint i = 0; i < _loopPointCallbacks.size();) {
- if ((_loopPointCallbacks[i].Callback)(_loopPointCallbacks[i].Data) == false) {
- _loopPointCallbacks.remove_at(i);
- } else
- i++;
- }
+ // Loop-Point callback
+ if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
+ _loopPointCallback = 0;
switch (animationDescriptionPtr->getAnimationType()) {
case AT_ONESHOT:
@@ -290,13 +281,9 @@ void Animation::frameNotification(int timeElapsed) {
forceRefresh();
if (animationDescriptionPtr->getFrame(_currentFrame).action != "") {
- // Action Callbacks
- for (uint i = 0; i < _actionCallbacks.size();) {
- if ((_actionCallbacks[i].Callback)(_actionCallbacks[i].Data) == false) {
- _actionCallbacks.remove_at(i);
- } else
- i++;
- }
+ // action callback
+ if (_actionCallback && !(_actionCallback)(getHandle()))
+ _actionCallback = 0;
}
}
@@ -315,9 +302,9 @@ void Animation::computeCurrentCharacteristics() {
BS_ASSERT(animationDescriptionPtr);
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(curFrame.fileName);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
BS_ASSERT(pResource);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
// Größe des Bitmaps auf die Animation übertragen
@@ -338,7 +325,7 @@ bool Animation::lockAllFrames() {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
BS_ASSERT(animationDescriptionPtr);
for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
- if (!Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->getFrame(i).fileName)) {
+ if (!Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName)) {
BS_LOG_ERRORLN("Could not lock all animation frames.");
return false;
}
@@ -356,14 +343,14 @@ bool Animation::unlockAllFrames() {
BS_ASSERT(animationDescriptionPtr);
for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
Resource *pResource;
- if (!(pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->getFrame(i).fileName))) {
+ if (!(pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName))) {
BS_LOG_ERRORLN("Could not unlock all animation frames.");
return false;
}
// Zwei mal freigeben um den Request von LockAllFrames() und den jetzigen Request aufzuheben
pResource->release();
- if (pResource->GetLockCount())
+ if (pResource->getLockCount())
pResource->release();
}
@@ -524,9 +511,9 @@ int Animation::computeXModifier() const {
BS_ASSERT(animationDescriptionPtr);
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(curFrame.fileName);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
BS_ASSERT(pResource);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
int result = curFrame.flipV ? - static_cast<int>((pBitmap->getWidth() - 1 - curFrame.hotspotX) * _scaleFactorX) :
@@ -542,9 +529,9 @@ int Animation::computeYModifier() const {
BS_ASSERT(animationDescriptionPtr);
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(curFrame.fileName);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
BS_ASSERT(pResource);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
int result = curFrame.flipH ? - static_cast<int>((pBitmap->getHeight() - 1 - curFrame.hotspotY) * _scaleFactorY) :
@@ -555,63 +542,6 @@ int Animation::computeYModifier() const {
return result;
}
-void Animation::registerActionCallback(ANIMATION_CALLBACK callback, uint data) {
- ANIMATION_CALLBACK_DATA cd;
- cd.Callback = callback;
- cd.Data = data;
- _actionCallbacks.push_back(cd);
-}
-
-void Animation::registerLoopPointCallback(ANIMATION_CALLBACK callback, uint data) {
- ANIMATION_CALLBACK_DATA cd;
- cd.Callback = callback;
- cd.Data = data;
- _loopPointCallbacks.push_back(cd);
-}
-
-void Animation::registerDeleteCallback(ANIMATION_CALLBACK callback, uint data) {
- ANIMATION_CALLBACK_DATA cd;
- cd.Callback = callback;
- cd.Data = data;
- _deleteCallbacks.push_back(cd);
-}
-
-void Animation::persistCallbackVector(OutputPersistenceBlock &writer, const Common::Array<ANIMATION_CALLBACK_DATA> &vector) {
- // Anzahl an Callbacks persistieren.
- writer.write(vector.size());
-
- // Alle Callbacks einzeln persistieren.
- Common::Array<ANIMATION_CALLBACK_DATA>::const_iterator it = vector.begin();
- while (it != vector.end()) {
- writer.write(CallbackRegistry::getInstance().resolveCallbackPointer((void (*)(int))it->Callback));
- writer.write(it->Data);
-
- ++it;
- }
-}
-
-void Animation::unpersistCallbackVector(InputPersistenceBlock &reader, Common::Array<ANIMATION_CALLBACK_DATA> &vector) {
- // Callbackvector leeren.
- vector.resize(0);
-
- // Anzahl an Callbacks einlesen.
- uint callbackCount;
- reader.read(callbackCount);
-
- // Alle Callbacks einzeln wieder herstellen.
- for (uint i = 0; i < callbackCount; ++i) {
- ANIMATION_CALLBACK_DATA callbackData;
-
- Common::String callbackFunctionName;
- reader.read(callbackFunctionName);
- callbackData.Callback = reinterpret_cast<ANIMATION_CALLBACK>(CallbackRegistry::getInstance().resolveCallbackFunction(callbackFunctionName));
-
- reader.read(callbackData.Data);
-
- vector.push_back(callbackData);
- }
-}
-
bool Animation::persist(OutputPersistenceBlock &writer) {
bool result = true;
@@ -632,7 +562,7 @@ bool Animation::persist(OutputPersistenceBlock &writer) {
if (_animationResourcePtr) {
uint marker = 0;
writer.write(marker);
- writer.write(_animationResourcePtr->getFileName());
+ writer.writeString(_animationResourcePtr->getFileName());
} else if (_animationTemplateHandle) {
uint marker = 1;
writer.write(marker);
@@ -644,9 +574,18 @@ bool Animation::persist(OutputPersistenceBlock &writer) {
//writer.write(_AnimationDescriptionPtr);
writer.write(_framesLocked);
- persistCallbackVector(writer, _loopPointCallbacks);
- persistCallbackVector(writer, _actionCallbacks);
- persistCallbackVector(writer, _deleteCallbacks);
+
+ // The following is only there to for compatibility with older saves
+ // resp. the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaLoopPointCB");
+ writer.write(getHandle());
+ writer.write((uint)1);
+ writer.writeString("LuaActionCB");
+ writer.write(getHandle());
+ writer.write((uint)1);
+ writer.writeString("LuaDeleteCB");
+ writer.write(getHandle());
result &= RenderObject::persistChildren(writer);
@@ -678,7 +617,7 @@ bool Animation::unpersist(InputPersistenceBlock &reader) {
reader.read(marker);
if (marker == 0) {
Common::String resourceFilename;
- reader.read(resourceFilename);
+ reader.readString(resourceFilename);
initializeAnimationResource(resourceFilename);
} else if (marker == 1) {
reader.read(_animationTemplateHandle);
@@ -690,9 +629,39 @@ bool Animation::unpersist(InputPersistenceBlock &reader) {
if (_framesLocked)
lockAllFrames();
- unpersistCallbackVector(reader, _loopPointCallbacks);
- unpersistCallbackVector(reader, _actionCallbacks);
- unpersistCallbackVector(reader, _deleteCallbacks);
+
+ // The following is only there to for compatibility with older saves
+ // resp. the original engine.
+ uint callbackCount;
+ Common::String callbackFunctionName;
+ uint callbackData;
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaLoopPointCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaActionCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaDeleteCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // Set the callbacks
+ setCallbacks();
result &= RenderObject::unpersistChildren(reader);
@@ -705,7 +674,7 @@ AnimationDescription *Animation::getAnimationDescription() const {
if (_animationResourcePtr)
return _animationResourcePtr;
else
- return AnimationTemplateRegistry::getInstance().resolveHandle(_animationTemplateHandle);
+ return AnimationTemplateRegistry::instance().resolveHandle(_animationTemplateHandle);
}
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animation.h b/engines/sword25/gfx/animation.h
index 0676c0c116..72fe7e2de8 100644
--- a/engines/sword25/gfx/animation.h
+++ b/engines/sword25/gfx/animation.h
@@ -151,9 +151,7 @@ public:
typedef bool (*ANIMATION_CALLBACK)(uint);
- void registerLoopPointCallback(ANIMATION_CALLBACK callback, uint data = 0);
- void registerActionCallback(ANIMATION_CALLBACK callback, uint data = 0);
- void registerDeleteCallback(ANIMATION_CALLBACK Callback, uint Data = 0);
+ void setCallbacks();
protected:
virtual bool doRender();
@@ -178,13 +176,9 @@ private:
uint _animationTemplateHandle;
bool _framesLocked;
- struct ANIMATION_CALLBACK_DATA {
- ANIMATION_CALLBACK Callback;
- uint Data;
- };
- Common::Array<ANIMATION_CALLBACK_DATA> _loopPointCallbacks;
- Common::Array<ANIMATION_CALLBACK_DATA> _actionCallbacks;
- Common::Array<ANIMATION_CALLBACK_DATA> _deleteCallbacks;
+ ANIMATION_CALLBACK _loopPointCallback;
+ ANIMATION_CALLBACK _actionCallback;
+ ANIMATION_CALLBACK _deleteCallback;
/**
@brief Lockt alle Frames.
@@ -216,8 +210,6 @@ private:
int computeYModifier() const;
void initMembers();
- void persistCallbackVector(OutputPersistenceBlock &writer, const Common::Array<ANIMATION_CALLBACK_DATA> &vector);
- void unpersistCallbackVector(InputPersistenceBlock &reader, Common::Array<ANIMATION_CALLBACK_DATA> &vector);
AnimationDescription *getAnimationDescription() const;
void initializeAnimationResource(const Common::String &fileName);
};
diff --git a/engines/sword25/gfx/animationdescription.h b/engines/sword25/gfx/animationdescription.h
index a52f5d3f68..88cbb23503 100644
--- a/engines/sword25/gfx/animationdescription.h
+++ b/engines/sword25/gfx/animationdescription.h
@@ -50,7 +50,7 @@ protected:
_scalingAllowed(true),
_alphaAllowed(true),
_colorModulationAllowed(true)
- {};
+ {}
public:
struct Frame {
diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp
index 93b5934041..6e5f683a2e 100644
--- a/engines/sword25/gfx/animationresource.cpp
+++ b/engines/sword25/gfx/animationresource.cpp
@@ -35,7 +35,6 @@
#include "sword25/gfx/animationresource.h"
#include "sword25/kernel/kernel.h"
-#include "sword25/kernel/string.h"
#include "sword25/package/packagemanager.h"
#include "sword25/gfx/bitmapresource.h"
@@ -54,8 +53,7 @@ AnimationResource::AnimationResource(const Common::String &filename) :
Common::XMLParser(),
_valid(false) {
// Get a pointer to the package manager
- Kernel *pKernel = Kernel::GetInstance();
- _pPackage = static_cast<PackageManager *>(pKernel->GetService("package"));
+ _pPackage = Kernel::getInstance()->getPackage();
BS_ASSERT(_pPackage);
// Switch to the folder the specified Xml fiile is in
@@ -117,7 +115,7 @@ bool AnimationResource::parseBooleanKey(Common::String s, bool &result) {
}
bool AnimationResource::parserCallback_animation(ParserNode *node) {
- if (!parseIntegerKey(node->values["fps"].c_str(), 1, &_FPS) || (_FPS < MIN_FPS) || (_FPS > MAX_FPS)) {
+ if (!parseIntegerKey(node->values["fps"], 1, &_FPS) || (_FPS < MIN_FPS) || (_FPS > MAX_FPS)) {
return parserError("Illegal or missing fps attribute in <animation> tag in \"%s\". Assuming default (\"%d\").",
getFileName().c_str(), DEFAULT_FPS);
}
@@ -212,7 +210,7 @@ AnimationResource::~AnimationResource() {
bool AnimationResource::precacheAllFrames() const {
Common::Array<Frame>::const_iterator iter = _frames.begin();
for (; iter != _frames.end(); ++iter) {
- if (!Kernel::GetInstance()->GetResourceManager()->PrecacheResource((*iter).fileName)) {
+ if (!Kernel::getInstance()->getResourceManager()->precacheResource((*iter).fileName)) {
BS_LOG_ERRORLN("Could not precache \"%s\".", (*iter).fileName.c_str());
return false;
}
@@ -233,7 +231,7 @@ bool AnimationResource::computeFeatures() {
Common::Array<Frame>::const_iterator iter = _frames.begin();
for (; iter != _frames.end(); ++iter) {
BitmapResource *pBitmap;
- if (!(pBitmap = static_cast<BitmapResource *>(Kernel::GetInstance()->GetResourceManager()->RequestResource((*iter).fileName)))) {
+ if (!(pBitmap = static_cast<BitmapResource *>(Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName)))) {
BS_LOG_ERRORLN("Could not request \"%s\".", (*iter).fileName.c_str());
return false;
}
diff --git a/engines/sword25/gfx/animationtemplate.cpp b/engines/sword25/gfx/animationtemplate.cpp
index 2019d19565..4a060dbad9 100644
--- a/engines/sword25/gfx/animationtemplate.cpp
+++ b/engines/sword25/gfx/animationtemplate.cpp
@@ -49,7 +49,7 @@ uint AnimationTemplate::create(const Common::String &sourceAnimation) {
AnimationTemplate *animationTemplatePtr = new AnimationTemplate(sourceAnimation);
if (animationTemplatePtr->isValid()) {
- return AnimationTemplateRegistry::getInstance().resolvePtr(animationTemplatePtr);
+ return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
} else {
delete animationTemplatePtr;
return 0;
@@ -60,7 +60,7 @@ uint AnimationTemplate::create(const AnimationTemplate &other) {
AnimationTemplate *animationTemplatePtr = new AnimationTemplate(other);
if (animationTemplatePtr->isValid()) {
- return AnimationTemplateRegistry::getInstance().resolvePtr(animationTemplatePtr);
+ return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
} else {
delete animationTemplatePtr;
return 0;
@@ -71,7 +71,7 @@ uint AnimationTemplate::create(InputPersistenceBlock &reader, uint handle) {
AnimationTemplate *animationTemplatePtr = new AnimationTemplate(reader, handle);
if (animationTemplatePtr->isValid()) {
- return AnimationTemplateRegistry::getInstance().resolvePtr(animationTemplatePtr);
+ return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
} else {
delete animationTemplatePtr;
return 0;
@@ -80,7 +80,7 @@ uint AnimationTemplate::create(InputPersistenceBlock &reader, uint handle) {
AnimationTemplate::AnimationTemplate(const Common::String &sourceAnimation) {
// Objekt registrieren.
- AnimationTemplateRegistry::getInstance().registerObject(this);
+ AnimationTemplateRegistry::instance().registerObject(this);
_valid = false;
@@ -93,7 +93,7 @@ AnimationTemplate::AnimationTemplate(const Common::String &sourceAnimation) {
AnimationTemplate::AnimationTemplate(const AnimationTemplate &other) : AnimationDescription() {
// Objekt registrieren.
- AnimationTemplateRegistry::getInstance().registerObject(this);
+ AnimationTemplateRegistry::instance().registerObject(this);
_valid = false;
@@ -118,16 +118,16 @@ AnimationTemplate::AnimationTemplate(const AnimationTemplate &other) : Animation
AnimationTemplate::AnimationTemplate(InputPersistenceBlock &reader, uint handle) {
// Objekt registrieren.
- AnimationTemplateRegistry::getInstance().registerObject(this, handle);
+ AnimationTemplateRegistry::instance().registerObject(this, handle);
// Objekt laden.
_valid = unpersist(reader);
}
AnimationResource *AnimationTemplate::requestSourceAnimation(const Common::String &sourceAnimation) const {
- ResourceManager *RMPtr = Kernel::GetInstance()->GetResourceManager();
+ ResourceManager *RMPtr = Kernel::getInstance()->getResourceManager();
Resource *resourcePtr;
- if (NULL == (resourcePtr = RMPtr->RequestResource(sourceAnimation)) || resourcePtr->GetType() != Resource::TYPE_ANIMATION) {
+ if (NULL == (resourcePtr = RMPtr->requestResource(sourceAnimation)) || resourcePtr->getType() != Resource::TYPE_ANIMATION) {
BS_LOG_ERRORLN("The resource \"%s\" could not be requested or is has an invalid type. The animation template can't be created.", sourceAnimation.c_str());
return 0;
}
@@ -141,7 +141,7 @@ AnimationTemplate::~AnimationTemplate() {
}
// Objekt deregistrieren
- AnimationTemplateRegistry::getInstance().deregisterObject(this);
+ AnimationTemplateRegistry::instance().deregisterObject(this);
}
void AnimationTemplate::addFrame(int index) {
@@ -195,13 +195,13 @@ bool AnimationTemplate::persist(OutputPersistenceBlock &writer) {
writer.write(Iter->hotspotY);
writer.write(Iter->flipV);
writer.write(Iter->flipH);
- writer.write(Iter->fileName);
- writer.write(Iter->action);
+ writer.writeString(Iter->fileName);
+ writer.writeString(Iter->action);
++Iter;
}
// Restliche Member persistieren.
- writer.write(_sourceAnimationPtr->getFileName());
+ writer.writeString(_sourceAnimationPtr->getFileName());
writer.write(_valid);
return Result;
@@ -224,15 +224,15 @@ bool AnimationTemplate::unpersist(InputPersistenceBlock &reader) {
reader.read(frame.hotspotY);
reader.read(frame.flipV);
reader.read(frame.flipH);
- reader.read(frame.fileName);
- reader.read(frame.action);
+ reader.readString(frame.fileName);
+ reader.readString(frame.action);
_frames.push_back(frame);
}
// Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
Common::String sourceAnimation;
- reader.read(sourceAnimation);
+ reader.readString(sourceAnimation);
_sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
reader.read(_valid);
diff --git a/engines/sword25/gfx/animationtemplateregistry.cpp b/engines/sword25/gfx/animationtemplateregistry.cpp
index 6f4af690c6..2245090f8f 100644
--- a/engines/sword25/gfx/animationtemplateregistry.cpp
+++ b/engines/sword25/gfx/animationtemplateregistry.cpp
@@ -39,9 +39,9 @@
#include "sword25/gfx/animationtemplateregistry.h"
#include "sword25/gfx/animationtemplate.h"
-namespace Sword25 {
+DECLARE_SINGLETON(Sword25::AnimationTemplateRegistry);
-Common::ScopedPtr<AnimationTemplateRegistry> AnimationTemplateRegistry::_instancePtr;
+namespace Sword25 {
void AnimationTemplateRegistry::logErrorLn(const char *message) const {
BS_LOG_ERRORLN(message);
diff --git a/engines/sword25/gfx/animationtemplateregistry.h b/engines/sword25/gfx/animationtemplateregistry.h
index 256cbab8cd..c5308bb124 100644
--- a/engines/sword25/gfx/animationtemplateregistry.h
+++ b/engines/sword25/gfx/animationtemplateregistry.h
@@ -39,19 +39,17 @@
#include "sword25/kernel/persistable.h"
#include "sword25/kernel/objectregistry.h"
-#include "common/ptr.h"
+#include "common/singleton.h"
namespace Sword25 {
class AnimationTemplate;
-class AnimationTemplateRegistry : public ObjectRegistry<AnimationTemplate>, public Persistable {
+class AnimationTemplateRegistry :
+ public ObjectRegistry<AnimationTemplate>,
+ public Persistable,
+ public Common::Singleton<AnimationTemplateRegistry> {
public:
- static AnimationTemplateRegistry &getInstance() {
- if (!_instancePtr.get())
- _instancePtr.reset(new AnimationTemplateRegistry);
- return *_instancePtr.get();
- }
virtual bool persist(OutputPersistenceBlock &writer);
virtual bool unpersist(InputPersistenceBlock &reader);
@@ -59,8 +57,6 @@ public:
private:
virtual void logErrorLn(const char *message) const;
virtual void logWarningLn(const char *message) const;
-
- static Common::ScopedPtr<AnimationTemplateRegistry> _instancePtr;
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/bitmap.cpp b/engines/sword25/gfx/bitmap.cpp
index 3a6ffb4a98..b5412c8276 100644
--- a/engines/sword25/gfx/bitmap.cpp
+++ b/engines/sword25/gfx/bitmap.cpp
@@ -32,26 +32,14 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/gfx/bitmap.h"
#include "sword25/kernel/outputpersistenceblock.h"
#include "sword25/kernel/inputpersistenceblock.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Logging
-// -----------------------------------------------------------------------------
-
#define BS_LOG_PREFIX "BITMAP"
-// -----------------------------------------------------------------------------
-// Konstruktion / Destruktion
-// -----------------------------------------------------------------------------
-
Bitmap::Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
RenderObject(parentPtr, type, handle),
_modulationColor(0xffffffff),
@@ -61,15 +49,9 @@ Bitmap::Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle)
_flipV(false) {
}
-// -----------------------------------------------------------------------------
-
Bitmap::~Bitmap() {
}
-// -----------------------------------------------------------------------------
-// Darstellungsart festlegen
-// -----------------------------------------------------------------------------
-
void Bitmap::setAlpha(int alpha) {
if (!isAlphaAllowed()) {
BS_LOG_WARNINGLN("Tried to set alpha value on a bitmap that does not support alpha blending. Call was ignored.");
@@ -94,8 +76,6 @@ void Bitmap::setAlpha(int alpha) {
}
}
-// -----------------------------------------------------------------------------
-
void Bitmap::setModulationColor(uint modulationColor) {
if (!isColorModulationAllowed()) {
BS_LOG_WARNINGLN("Tried to set modulation color of a bitmap that does not support color modulation. Call was ignored.");
@@ -109,15 +89,11 @@ void Bitmap::setModulationColor(uint modulationColor) {
}
}
-// -----------------------------------------------------------------------------
-
void Bitmap::setScaleFactor(float scaleFactor) {
setScaleFactorX(scaleFactor);
setScaleFactorY(scaleFactor);
}
-// -----------------------------------------------------------------------------
-
void Bitmap::setScaleFactorX(float scaleFactorX) {
if (!isScalingAllowed()) {
BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
@@ -134,12 +110,12 @@ void Bitmap::setScaleFactorX(float scaleFactorX) {
_width = static_cast<int>(_originalWidth * _scaleFactorX);
if (_scaleFactorX <= 0.0f)
_scaleFactorX = 0.001f;
+ if (_width <= 0)
+ _width = 1;
forceRefresh();
}
}
-// -----------------------------------------------------------------------------
-
void Bitmap::setScaleFactorY(float scaleFactorY) {
if (!isScalingAllowed()) {
BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
@@ -156,28 +132,22 @@ void Bitmap::setScaleFactorY(float scaleFactorY) {
_height = static_cast<int>(_originalHeight * scaleFactorY);
if (_scaleFactorY <= 0.0f)
_scaleFactorY = 0.001f;
+ if (_height <= 0)
+ _height = 1;
forceRefresh();
}
}
-// -----------------------------------------------------------------------------
-
void Bitmap::setFlipH(bool flipH) {
_flipH = flipH;
forceRefresh();
}
-// -----------------------------------------------------------------------------
-
void Bitmap::setFlipV(bool flipV) {
_flipV = flipV;
forceRefresh();
}
-// -----------------------------------------------------------------------------
-// Persistenz
-// -----------------------------------------------------------------------------
-
bool Bitmap::persist(OutputPersistenceBlock &writer) {
bool result = true;
@@ -193,8 +163,6 @@ bool Bitmap::persist(OutputPersistenceBlock &writer) {
return result;
}
-// -----------------------------------------------------------------------------
-
bool Bitmap::unpersist(InputPersistenceBlock &reader) {
bool result = true;
diff --git a/engines/sword25/gfx/bitmap.h b/engines/sword25/gfx/bitmap.h
index a00baf37a4..741269c423 100644
--- a/engines/sword25/gfx/bitmap.h
+++ b/engines/sword25/gfx/bitmap.h
@@ -35,19 +35,11 @@
#ifndef SWORD25_BITMAP_H
#define SWORD25_BITMAP_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/gfx/renderobject.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Klassendeklaration
-// -----------------------------------------------------------------------------
-
class Bitmap : public RenderObject {
protected:
Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle = 0);
diff --git a/engines/sword25/gfx/bitmapresource.cpp b/engines/sword25/gfx/bitmapresource.cpp
index 46e6ca77ce..cf3c85e3ac 100644
--- a/engines/sword25/gfx/bitmapresource.cpp
+++ b/engines/sword25/gfx/bitmapresource.cpp
@@ -35,16 +35,12 @@
#include "sword25/gfx/bitmapresource.h"
#include "sword25/kernel/kernel.h"
#include "sword25/gfx/graphicengine.h"
-#include "sword25/gfx/image/imageloader.h"
#include "sword25/package/packagemanager.h"
namespace Sword25 {
#define BS_LOG_PREFIX "BITMAP"
-// Konstruktion / Destruktion
-// --------------------------
-
BitmapResource::BitmapResource(const Common::String &filename, Image *pImage) :
_valid(false),
_pImage(pImage),
@@ -56,8 +52,6 @@ BitmapResource::~BitmapResource() {
delete _pImage;
}
-// -----------------------------------------------------------------------------
-
uint BitmapResource::getPixel(int x, int y) const {
BS_ASSERT(x >= 0 && x < _pImage->getWidth());
BS_ASSERT(y >= 0 && y < _pImage->getHeight());
diff --git a/engines/sword25/gfx/bitmapresource.h b/engines/sword25/gfx/bitmapresource.h
index 1054770e79..37849f918e 100644
--- a/engines/sword25/gfx/bitmapresource.h
+++ b/engines/sword25/gfx/bitmapresource.h
@@ -35,7 +35,6 @@
#ifndef SWORD25_BITMAP_RESOURCE_H
#define SWORD25_BITMAP_RESOURCE_H
-// Includes
#include "sword25/kernel/common.h"
#include "sword25/kernel/resource.h"
#include "sword25/gfx/image/image.h"
@@ -204,8 +203,8 @@ public:
}
private:
- Image *_pImage;
- bool _valid;
+ Image *_pImage;
+ bool _valid;
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/dynamicbitmap.cpp b/engines/sword25/gfx/dynamicbitmap.cpp
index 91d46e99f4..612e370712 100644
--- a/engines/sword25/gfx/dynamicbitmap.cpp
+++ b/engines/sword25/gfx/dynamicbitmap.cpp
@@ -32,10 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/gfx/dynamicbitmap.h"
#include "sword25/gfx/bitmapresource.h"
#include "sword25/package/packagemanager.h"
@@ -43,16 +39,8 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Logging
-// -----------------------------------------------------------------------------
-
#define BS_LOG_PREFIX "DYNAMICBITMAP"
-// -----------------------------------------------------------------------------
-// Konstruktion / Destruktion
-// -----------------------------------------------------------------------------
-
DynamicBitmap::DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height) :
Bitmap(parentPtr, TYPE_DYNAMICBITMAP) {
// Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
@@ -61,15 +49,11 @@ DynamicBitmap::DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width
_initSuccess = createRenderedImage(width, height);
}
-// -----------------------------------------------------------------------------
-
DynamicBitmap::DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
Bitmap(parentPtr, TYPE_DYNAMICBITMAP, handle) {
_initSuccess = unpersist(reader);
}
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::createRenderedImage(uint width, uint height) {
// RenderedImage mit den gewünschten Maßen erstellen
bool result = false;
@@ -81,13 +65,9 @@ bool DynamicBitmap::createRenderedImage(uint width, uint height) {
return result;
}
-// -----------------------------------------------------------------------------
-
DynamicBitmap::~DynamicBitmap() {
}
-// -----------------------------------------------------------------------------
-
uint DynamicBitmap::getPixel(int x, int y) const {
BS_ASSERT(x >= 0 && x < _width);
BS_ASSERT(y >= 0 && y < _height);
@@ -95,11 +75,9 @@ uint DynamicBitmap::getPixel(int x, int y) const {
return _image->getPixel(x, y);
}
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::doRender() {
// Framebufferobjekt holen
- GraphicEngine *pGfx = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
BS_ASSERT(pGfx);
// Bitmap zeichnen
@@ -119,42 +97,26 @@ bool DynamicBitmap::doRender() {
return result;
}
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
return _image->setContent(pixeldata, size, offset, stride);
}
-// -----------------------------------------------------------------------------
-// Auskunftsmethoden
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::isScalingAllowed() const {
return _image->isScalingAllowed();
}
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::isAlphaAllowed() const {
return _image->isAlphaAllowed();
}
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::isColorModulationAllowed() const {
return _image->isColorModulationAllowed();
}
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::isSetContentAllowed() const {
return true;
}
-// -----------------------------------------------------------------------------
-// Persistenz
-// -----------------------------------------------------------------------------
-
bool DynamicBitmap::persist(OutputPersistenceBlock &writer) {
bool result = true;
diff --git a/engines/sword25/gfx/dynamicbitmap.h b/engines/sword25/gfx/dynamicbitmap.h
index a02769fb57..1737bdf5fc 100644
--- a/engines/sword25/gfx/dynamicbitmap.h
+++ b/engines/sword25/gfx/dynamicbitmap.h
@@ -35,11 +35,6 @@
#ifndef SWORD25_DYNAMIC_BITMAP_H
#define SWORD25_DYNAMIC_BITMAP_H
-
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/gfx/bitmap.h"
#include "sword25/gfx/image/renderedimage.h"
@@ -48,10 +43,6 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Klassendeklaration
-// -----------------------------------------------------------------------------
-
class DynamicBitmap : public Bitmap {
friend class RenderObject;
@@ -60,18 +51,18 @@ public:
virtual uint getPixel(int x, int y) const;
- virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
- virtual bool isScalingAllowed() const;
- virtual bool isAlphaAllowed() const;
- virtual bool isColorModulationAllowed() const;
- virtual bool isSetContentAllowed() const;
+ virtual bool isScalingAllowed() const;
+ virtual bool isAlphaAllowed() const;
+ virtual bool isColorModulationAllowed() const;
+ virtual bool isSetContentAllowed() const;
- virtual bool persist(OutputPersistenceBlock &writer);
- virtual bool unpersist(InputPersistenceBlock &reader);
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
protected:
- virtual bool doRender();
+ virtual bool doRender();
private:
DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height);
diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp
index 9f23133a71..dbb9c67fe5 100644
--- a/engines/sword25/gfx/fontresource.cpp
+++ b/engines/sword25/gfx/fontresource.cpp
@@ -34,38 +34,27 @@
#define BS_LOG_PREFIX "FONTRESOURCE"
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/kernel.h"
-#include "sword25/kernel/string.h"
#include "sword25/package/packagemanager.h"
#include "sword25/gfx/fontresource.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Konstanten
-// -----------------------------------------------------------------------------
-
-static const uint DEFAULT_LINEHEIGHT = 20;
-static const uint DEFAULT_GAPWIDTH = 1;
-
-// -----------------------------------------------------------------------------
-// Konstruktion / Destruktion
-// -----------------------------------------------------------------------------
+enum {
+ DEFAULT_LINEHEIGHT = 20,
+ DEFAULT_GAPWIDTH = 1
+};
-FontResource::FontResource(Kernel *pKernel, const Common::String &FileName) :
+FontResource::FontResource(Kernel *pKernel, const Common::String &fileName) :
_pKernel(pKernel),
- _Valid(false),
- Resource(FileName, Resource::TYPE_FONT),
+ _valid(false),
+ Resource(fileName, Resource::TYPE_FONT),
Common::XMLParser() {
// Get a pointer to the package manager
BS_ASSERT(_pKernel);
- PackageManager *pPackage = static_cast<PackageManager *>(_pKernel->GetService("package"));
+ PackageManager *pPackage = _pKernel->getPackage();
BS_ASSERT(pPackage);
// Load the contents of the file
@@ -80,73 +69,69 @@ FontResource::FontResource(Kernel *pKernel, const Common::String &FileName) :
if (!loadBuffer((const byte *)xmlData, fileSize))
return;
- _Valid = parse();
+ _valid = parse();
close();
free(xmlData);
}
-// -----------------------------------------------------------------------------
-
bool FontResource::parserCallback_font(ParserNode *node) {
// Get the attributes of the font
Common::String bitmapFilename = node->values["bitmap"];
- if (!parseIntegerKey(node->values["lineheight"].c_str(), 1, &_LineHeight)) {
+ if (!parseIntegerKey(node->values["lineheight"], 1, &_lineHeight)) {
BS_LOG_WARNINGLN("Illegal or missing lineheight attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
getFileName().c_str(), DEFAULT_LINEHEIGHT);
- _LineHeight = DEFAULT_LINEHEIGHT;
+ _lineHeight = DEFAULT_LINEHEIGHT;
}
- if (!parseIntegerKey(node->values["gap"].c_str(), 1, &_GapWidth)) {
+ if (!parseIntegerKey(node->values["gap"], 1, &_gapWidth)) {
BS_LOG_WARNINGLN("Illegal or missing gap attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
getFileName().c_str(), DEFAULT_GAPWIDTH);
- _GapWidth = DEFAULT_GAPWIDTH;
+ _gapWidth = DEFAULT_GAPWIDTH;
}
// Get a reference to the package manager
BS_ASSERT(_pKernel);
- PackageManager *pPackage = static_cast<PackageManager *>(_pKernel->GetService("package"));
+ PackageManager *pPackage = _pKernel->getPackage();
BS_ASSERT(pPackage);
// Get the full path and filename for the bitmap resource
- _BitmapFileName = pPackage->getAbsolutePath(bitmapFilename);
- if (_BitmapFileName == "") {
+ _bitmapFileName = pPackage->getAbsolutePath(bitmapFilename);
+ if (_bitmapFileName == "") {
BS_LOG_ERRORLN("Image file \"%s\" was specified in <font> tag of \"%s\" but could not be found.",
- _BitmapFileName.c_str(), getFileName().c_str());
+ _bitmapFileName.c_str(), getFileName().c_str());
}
// Pre-cache the resource
- if (!_pKernel->GetResourceManager()->PrecacheResource(_BitmapFileName)) {
- BS_LOG_ERRORLN("Could not precache \"%s\".", _BitmapFileName.c_str());
+ if (!_pKernel->getResourceManager()->precacheResource(_bitmapFileName)) {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", _bitmapFileName.c_str());
}
return true;
}
-// -----------------------------------------------------------------------------
-
bool FontResource::parserCallback_character(ParserNode *node) {
// Get the attributes of the character
int charCode, top, left, right, bottom;
- if (!parseIntegerKey(node->values["code"].c_str(), 1, &charCode) || (charCode < 0) || (charCode >= 256)) {
+ if (!parseIntegerKey(node->values["code"], 1, &charCode) || (charCode < 0) || (charCode >= 256)) {
return parserError("Illegal or missing code attribute in <character> tag in \"%s\".", getFileName().c_str());
}
- if (!parseIntegerKey(node->values["top"].c_str(), 1, &top) || (top < 0)) {
+ if (!parseIntegerKey(node->values["top"], 1, &top) || (top < 0)) {
return parserError("Illegal or missing top attribute in <character> tag in \"%s\".", getFileName().c_str());
}
- if (!parseIntegerKey(node->values["left"].c_str(), 1, &left) || (left < 0)) {
+ if (!parseIntegerKey(node->values["left"], 1, &left) || (left < 0)) {
return parserError("Illegal or missing left attribute in <character> tag in \"%s\".", getFileName().c_str());
}
- if (!parseIntegerKey(node->values["right"].c_str(), 1, &right) || (right < 0)) {
+ if (!parseIntegerKey(node->values["right"], 1, &right) || (right < 0)) {
return parserError("Illegal or missing right attribute in <character> tag in \"%s\".", getFileName().c_str());
}
- if (!parseIntegerKey(node->values["bottom"].c_str(), 1, &bottom) || (bottom < 0)) {
+ if (!parseIntegerKey(node->values["bottom"], 1, &bottom) || (bottom < 0)) {
return parserError("Illegal or missing bottom attribute in <character> tag in \"%s\".", getFileName().c_str());
}
- this->_CharacterRects[charCode] = Common::Rect(left, top, right, bottom);
+ this->_characterRects[charCode] = Common::Rect(left, top, right, bottom);
return true;
}
diff --git a/engines/sword25/gfx/fontresource.h b/engines/sword25/gfx/fontresource.h
index cfb0251084..19c44d0ade 100644
--- a/engines/sword25/gfx/fontresource.h
+++ b/engines/sword25/gfx/fontresource.h
@@ -35,10 +35,6 @@
#ifndef SWORD25_FONTRESOURCE_H
#define SWORD25_FONTRESOURCE_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "common/scummsys.h"
#include "common/rect.h"
#include "common/xmlparser.h"
@@ -47,16 +43,8 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Forward declarations
-// -----------------------------------------------------------------------------
-
class Kernel;
-// -----------------------------------------------------------------------------
-// Klassendefinition
-// -----------------------------------------------------------------------------
-
class FontResource : public Resource, Common::XMLParser {
public:
/**
@@ -65,15 +53,15 @@ public:
@param FileName der Dateiname der zu ladenen Resource
@remark Wenn der Konstruktor erfolgreich ausgeführt werden konnte gibt die Methode IsValid true zurück.
*/
- FontResource(Kernel *pKernel, const Common::String &FileName);
+ FontResource(Kernel *pKernel, const Common::String &fileName);
/**
@brief Gibt true zurück, wenn das Objekt korrekt initialisiert wurde.
Diese Methode kann dazu benutzt werden um festzustellen, ob der Konstruktor erfolgreich ausgeführt wurde.
*/
- bool IsValid() const {
- return _Valid;
+ bool isValid() const {
+ return _valid;
}
/**
@@ -81,8 +69,8 @@ public:
Die Zeilenhöhe ist der Wert, der zur Y-Koordinate addiert wird, wenn ein Zeilenumbruch auftritt.
*/
- int GetLineHeight() const {
- return _LineHeight;
+ int getLineHeight() const {
+ return _lineHeight;
}
/**
@@ -90,8 +78,8 @@ public:
Der Buchstabenabstand ist der Wert, der zwischen zwei Buchstaben freigelassen wird.
*/
- int GetGapWidth() const {
- return _GapWidth;
+ int getGapWidth() const {
+ return _gapWidth;
}
/**
@@ -99,25 +87,25 @@ public:
@param Character der ASCII-Code des Zeichens
@return Das Bounding-Rect des übergebenen Zeichens auf der Charactermap.
*/
- const Common::Rect &GetCharacterRect(int Character) const {
- BS_ASSERT(Character >= 0 && Character < 256);
- return _CharacterRects[Character];
+ const Common::Rect &getCharacterRect(int character) const {
+ BS_ASSERT(character >= 0 && character < 256);
+ return _characterRects[character];
}
/**
@brief Gibt den Dateinamen der Charactermap zurück.
*/
- const Common::String &GetCharactermapFileName() const {
- return _BitmapFileName;
+ const Common::String &getCharactermapFileName() const {
+ return _bitmapFileName;
}
private:
Kernel *_pKernel;
- bool _Valid;
- Common::String _BitmapFileName;
- int _LineHeight;
- int _GapWidth;
- Common::Rect _CharacterRects[256];
+ bool _valid;
+ Common::String _bitmapFileName;
+ int _lineHeight;
+ int _gapWidth;
+ Common::Rect _characterRects[256];
// Parser
CUSTOM_XML_PARSER(FontResource) {
@@ -139,14 +127,6 @@ private:
// Parser callback methods
bool parserCallback_font(ParserNode *node);
bool parserCallback_character(ParserNode *node);
-
- // -----------------------------------------------------------------------------
- // Hilfsmethoden
- // -----------------------------------------------------------------------------
-
-// bool _ParseXMLDocument(const Common::String &FileName, TiXmlDocument &Doc) const;
-// bool _ParseFontTag(TiXmlElement &Tag, Common::String &BitmapFileName, int &LineHeight, int &GapWidth) const;
-// bool _ParseCharacterTag(TiXmlElement &Tag, int &Code, Common::Rect &Rect) const;
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/framecounter.cpp b/engines/sword25/gfx/framecounter.cpp
index 15bc7d00ea..07415cc2dc 100644
--- a/engines/sword25/gfx/framecounter.cpp
+++ b/engines/sword25/gfx/framecounter.cpp
@@ -37,30 +37,30 @@
namespace Sword25 {
-Framecounter::Framecounter(int UpdateFrequency) :
- m_FPS(0),
- m_FPSCount(0),
- m_LastUpdateTime(-1) {
- SetUpdateFrequency(UpdateFrequency);
+Framecounter::Framecounter(int updateFrequency) :
+ _FPS(0),
+ _FPSCount(0),
+ _lastUpdateTime(-1) {
+ setUpdateFrequency(updateFrequency);
}
-void Framecounter::Update() {
+void Framecounter::update() {
// Aktuellen Systemtimerstand auslesen
- uint64_t Timer = g_system->getMillis() * 1000;
+ uint64 timer = g_system->getMillis() * 1000;
// Falls m_LastUpdateTime == -1 ist, wird der Frame-Counter zum ersten Mal aufgerufen und der aktuelle Systemtimer als erster
// Messzeitpunkt genommen.
- if (m_LastUpdateTime == -1)
- m_LastUpdateTime = Timer;
+ if (_lastUpdateTime == -1)
+ _lastUpdateTime = timer;
else {
// Die Anzahl der Frames im aktuellen Messzeitraum wird erhöht.
- m_FPSCount++;
+ _FPSCount++;
// Falls der Messzeitraum verstrichen ist, wird die durchschnittliche Framerate berechnet und ein neuer Messzeitraum begonnen.
- if (Timer - m_LastUpdateTime >= m_UpdateDelay) {
- m_FPS = static_cast<int>((1000000 * (uint64_t)m_FPSCount) / (Timer - m_LastUpdateTime));
- m_LastUpdateTime = Timer;
- m_FPSCount = 0;
+ if (timer - _lastUpdateTime >= _updateDelay) {
+ _FPS = static_cast<int>((1000000 * (uint64)_FPSCount) / (timer - _lastUpdateTime));
+ _lastUpdateTime = timer;
+ _FPSCount = 0;
}
}
}
diff --git a/engines/sword25/gfx/framecounter.h b/engines/sword25/gfx/framecounter.h
index 8a8402a3bb..994950573f 100644
--- a/engines/sword25/gfx/framecounter.h
+++ b/engines/sword25/gfx/framecounter.h
@@ -37,7 +37,6 @@
// Includes
#include "sword25/kernel/common.h"
-#include "sword25/kernel/bs_stdint.h"
namespace Sword25 {
@@ -46,6 +45,12 @@ namespace Sword25 {
*/
class Framecounter {
private:
+
+ // TODO: This class should be rewritten based on Audio::Timestamp,
+ // which provides higher accuracy and avoids using 64 bit data types.
+ typedef unsigned long long uint64;
+ typedef signed long long int64;
+
enum {
DEFAULT_UPDATE_FREQUENCY = 10
};
@@ -56,37 +61,37 @@ public:
* @param UpdateFrequency Specifies how often the frame counter should be updated in a sceond.
* The default value is 10.
*/
- Framecounter(int UpdateFrequency = DEFAULT_UPDATE_FREQUENCY);
+ Framecounter(int updateFrequency = DEFAULT_UPDATE_FREQUENCY);
/**
* Determines how often the frame counter should be updated in a second.
* @param UpdateFrequency Specifies how often the frame counter should be updated in a second.
*/
- inline void SetUpdateFrequency(int UpdateFrequency);
+ inline void setUpdateFrequency(int updateFrequency);
/**
* This method must be called once per frame.
*/
- void Update();
+ void update();
/**
* Returns the current FPS value.
*/
- int GetFPS() const {
- return m_FPS;
+ int getFPS() const {
+ return _FPS;
}
private:
- int m_FPS;
- int m_FPSCount;
- int64_t m_LastUpdateTime;
- uint64_t m_UpdateDelay;
+ int _FPS;
+ int _FPSCount;
+ int64 _lastUpdateTime;
+ uint64 _updateDelay;
};
// Inlines
-void Framecounter::SetUpdateFrequency(int UpdateFrequency) {
+void Framecounter::setUpdateFrequency(int updateFrequency) {
// Frequency in time (converted to microseconds)
- m_UpdateDelay = 1000000 / UpdateFrequency;
+ _updateDelay = 1000000 / updateFrequency;
}
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp
index ea0c8c82c5..44b9932de1 100644
--- a/engines/sword25/gfx/graphicengine.cpp
+++ b/engines/sword25/gfx/graphicengine.cpp
@@ -52,108 +52,90 @@
#include "sword25/gfx/graphicengine.h"
-namespace Lua {
-extern "C"
-{
#include "sword25/util/lua/lua.h"
#include "sword25/util/lua/lauxlib.h"
-}
-}
-
-namespace {
-const int BIT_DEPTH = 32;
-const int BACKBUFFER_COUNT = 1;
-const Common::String PNG_EXTENSION(".png");
-const Common::String PNG_S_EXTENSION("_s.png");
-const Common::String ANI_EXTENSION("_ani.xml");
-const Common::String FNT_EXTENSION("_fnt.xml");
-const Common::String SWF_EXTENSION(".swf");
-const Common::String B25S_EXTENSION(".b25s");
-}
+enum {
+ BIT_DEPTH = 32,
+ BACKBUFFER_COUNT = 1
+};
namespace Sword25 {
-using namespace Lua;
-
static const uint FRAMETIME_SAMPLE_COUNT = 5; // Anzahl der Framezeiten über die, die Framezeit gemittelt wird
GraphicEngine::GraphicEngine(Kernel *pKernel) :
- m_Width(0),
- m_Height(0),
- m_BitDepth(0),
- m_Windowed(0),
- m_LastTimeStamp((uint64) - 1), // max. BS_INT64 um beim ersten Aufruf von _UpdateLastFrameDuration() einen Reset zu erzwingen
- m_LastFrameDuration(0),
- m_TimerActive(true),
- m_FrameTimeSampleSlot(0),
- m_RepaintedPixels(0),
+ _width(0),
+ _height(0),
+ _bitDepth(0),
+ _windowed(0),
+ _lastTimeStamp((uint) -1), // max. BS_INT64 um beim ersten Aufruf von _UpdateLastFrameDuration() einen Reset zu erzwingen
+ _lastFrameDuration(0),
+ _timerActive(true),
+ _frameTimeSampleSlot(0),
+ _repaintedPixels(0),
_thumbnail(NULL),
ResourceService(pKernel) {
- m_FrameTimeSamples.resize(FRAMETIME_SAMPLE_COUNT);
+ _frameTimeSamples.resize(FRAMETIME_SAMPLE_COUNT);
- if (!RegisterScriptBindings())
+ if (!registerScriptBindings())
BS_LOG_ERRORLN("Script bindings could not be registered.");
else
BS_LOGLN("Script bindings registered.");
}
GraphicEngine::~GraphicEngine() {
+ unregisterScriptBindings();
_backSurface.free();
_frameBuffer.free();
delete _thumbnail;
}
-Service *GraphicEngine_CreateObject(Kernel *pKernel) {
- return new GraphicEngine(pKernel);
-}
-
-bool GraphicEngine::Init(int Width, int Height, int BitDepth, int BackbufferCount, bool Windowed) {
+bool GraphicEngine::init(int width, int height, int bitDepth, int backbufferCount, bool isWindowed_) {
// Warnung ausgeben, wenn eine nicht unterstützte Bittiefe gewählt wurde.
- if (BitDepth != BIT_DEPTH) {
- BS_LOG_WARNINGLN("Can't use a bit depth of %d (not supported). Falling back to %d.", BitDepth, BIT_DEPTH);
- m_BitDepth = BIT_DEPTH;
+ if (bitDepth != BIT_DEPTH) {
+ BS_LOG_WARNINGLN("Can't use a bit depth of %d (not supported). Falling back to %d.", bitDepth, BIT_DEPTH);
+ _bitDepth = BIT_DEPTH;
}
// Warnung ausgeben, wenn nicht genau ein Backbuffer gewählt wurde.
- if (BackbufferCount != BACKBUFFER_COUNT) {
- BS_LOG_WARNINGLN("Can't use %d backbuffers (not supported). Falling back to %d.", BackbufferCount, BACKBUFFER_COUNT);
- BackbufferCount = BACKBUFFER_COUNT;
+ if (backbufferCount != BACKBUFFER_COUNT) {
+ BS_LOG_WARNINGLN("Can't use %d backbuffers (not supported). Falling back to %d.", backbufferCount, BACKBUFFER_COUNT);
+ backbufferCount = BACKBUFFER_COUNT;
}
// Parameter in lokale Variablen kopieren
- m_Width = Width;
- m_Height = Height;
- m_BitDepth = BitDepth;
- m_Windowed = Windowed;
- m_ScreenRect.left = 0;
- m_ScreenRect.top = 0;
- m_ScreenRect.right = m_Width;
- m_ScreenRect.bottom = m_Height;
-
- _backSurface.create(Width, Height, 4);
- _frameBuffer.create(Width, Height, 4);
+ _width = width;
+ _height = height;
+ _bitDepth = bitDepth;
+ _windowed = isWindowed_;
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ _screenRect.right = _width;
+ _screenRect.bottom = _height;
+
+ _backSurface.create(width, height, 4);
+ _frameBuffer.create(width, height, 4);
// Standardmäßig ist Vsync an.
- SetVsync(true);
+ setVsync(true);
// Layer-Manager initialisieren.
- _renderObjectManagerPtr.reset(new RenderObjectManager(Width, Height, BackbufferCount + 1));
+ _renderObjectManagerPtr.reset(new RenderObjectManager(width, height, backbufferCount + 1));
// Hauptpanel erstellen
- m_MainPanelPtr = _renderObjectManagerPtr->getTreeRoot()->addPanel(Width, Height, BS_ARGB(0, 0, 0, 0));
- if (!m_MainPanelPtr.isValid()) return false;
- m_MainPanelPtr->setVisible(true);
+ _mainPanelPtr = _renderObjectManagerPtr->getTreeRoot()->addPanel(width, height, BS_ARGB(0, 0, 0, 0));
+ if (!_mainPanelPtr.isValid())
+ return false;
+ _mainPanelPtr->setVisible(true);
return true;
}
-// -----------------------------------------------------------------------------
-
-bool GraphicEngine::StartFrame(bool UpdateAll) {
+bool GraphicEngine::startFrame(bool updateAll) {
// Berechnen, wie viel Zeit seit dem letzten Frame vergangen ist.
// Dieser Wert kann über GetLastFrameDuration() von Modulen abgefragt werden, die zeitabhängig arbeiten.
- UpdateLastFrameDuration();
+ updateLastFrameDuration();
// Den Layer-Manager auf den nächsten Frame vorbereiten
_renderObjectManagerPtr->startFrame();
@@ -161,9 +143,7 @@ bool GraphicEngine::StartFrame(bool UpdateAll) {
return true;
}
-// -----------------------------------------------------------------------------
-
-bool GraphicEngine::EndFrame() {
+bool GraphicEngine::endFrame() {
// Scene zeichnen
_renderObjectManagerPtr->render();
@@ -179,7 +159,7 @@ bool GraphicEngine::EndFrame() {
g_system->updateScreen();
// Debug-Lines zeichnen
- if (!m_DebugLines.empty()) {
+ if (!_debugLines.empty()) {
#if 0
glEnable(GL_LINE_SMOOTH);
glBegin(GL_LINES);
@@ -201,55 +181,76 @@ bool GraphicEngine::EndFrame() {
warning("STUB: Drawing debug lines");
- m_DebugLines.clear();
+ _debugLines.clear();
}
// Framecounter aktualisieren
- m_FPSCounter.Update();
+ _FPSCounter.update();
return true;
}
-// -----------------------------------------------------------------------------
-
-RenderObjectPtr<Panel> GraphicEngine::GetMainPanel() {
- return m_MainPanelPtr;
+RenderObjectPtr<Panel> GraphicEngine::getMainPanel() {
+ return _mainPanelPtr;
}
-// -----------------------------------------------------------------------------
-
-void GraphicEngine::SetVsync(bool Vsync) {
- warning("STUB: SetVsync(%d)", Vsync);
+void GraphicEngine::setVsync(bool vsync) {
+ warning("STUB: SetVsync(%d)", vsync);
}
-// -----------------------------------------------------------------------------
-
-bool GraphicEngine::GetVsync() const {
- warning("STUB: GetVsync()");
+bool GraphicEngine::getVsync() const {
+ warning("STUB: getVsync()");
return true;
}
-// -----------------------------------------------------------------------------
-
bool GraphicEngine::fill(const Common::Rect *fillRectPtr, uint color) {
- Common::Rect rect(m_Width - 1, m_Height - 1);
+ Common::Rect rect(_width - 1, _height - 1);
+
+ int ca = (color >> 24) & 0xff;
+
+ if (ca == 0)
+ return true;
+
+ int cr = (color >> 16) & 0xff;
+ int cg = (color >> 8) & 0xff;
+ int cb = (color >> 0) & 0xff;
if (fillRectPtr) {
rect = *fillRectPtr;
}
- if (fillRectPtr->width() > 0 && fillRectPtr->height() > 0) {
- _backSurface.fillRect(rect, color);
- g_system->copyRectToScreen((byte *)_backSurface.getBasePtr(fillRectPtr->left, fillRectPtr->top), _backSurface.pitch, fillRectPtr->left, fillRectPtr->top, fillRectPtr->width(), fillRectPtr->height());
+ if (rect.width() > 0 && rect.height() > 0) {
+ if (ca == 0xff) {
+ _backSurface.fillRect(rect, color);
+ } else {
+ byte *outo = (byte *)_backSurface.getBasePtr(rect.left, rect.top);
+ byte *out;
+
+ for (int i = rect.top; i < rect.bottom; i++) {
+ out = outo;
+ for (int j = rect.left; j < rect.right; j++) {
+ *out += (byte)(((cb - *out) * ca) >> 8);
+ out++;
+ *out += (byte)(((cg - *out) * ca) >> 8);
+ out++;
+ *out += (byte)(((cr - *out) * ca) >> 8);
+ out++;
+ *out = 255;
+ out++;
+ }
+
+ outo += _backSurface.pitch;
+ }
+ }
+
+ g_system->copyRectToScreen((byte *)_backSurface.getBasePtr(rect.left, rect.top), _backSurface.pitch, rect.left, rect.top, rect.width(), rect.height());
}
return true;
}
-// -----------------------------------------------------------------------------
-
-Graphics::Surface *GraphicEngine::GetScreenshot() {
+Graphics::Surface *GraphicEngine::getScreenshot() {
return &_frameBuffer;
}
@@ -257,19 +258,19 @@ Graphics::Surface *GraphicEngine::GetScreenshot() {
// RESOURCE MANAGING
// -----------------------------------------------------------------------------
-Resource *GraphicEngine::loadResource(const Common::String &FileName) {
- BS_ASSERT(canLoadResource(FileName));
+Resource *GraphicEngine::loadResource(const Common::String &filename) {
+ BS_ASSERT(canLoadResource(filename));
- // Bild für den Softwarebuffer laden
- if (FileName.hasSuffix(PNG_S_EXTENSION)) {
- bool Result = false;
- SWImage *pImage = new SWImage(FileName, Result);
- if (!Result) {
+ // Load image for "software buffer" (FIXME: Whatever that means?)
+ if (filename.hasSuffix("_s.png")) {
+ bool result = false;
+ SWImage *pImage = new SWImage(filename, result);
+ if (!result) {
delete pImage;
return 0;
}
- BitmapResource *pResource = new BitmapResource(FileName, pImage);
+ BitmapResource *pResource = new BitmapResource(filename, pImage);
if (!pResource->isValid()) {
delete pResource;
return 0;
@@ -278,16 +279,16 @@ Resource *GraphicEngine::loadResource(const Common::String &FileName) {
return pResource;
}
- // Sprite-Bild laden
- if (FileName.hasSuffix(PNG_EXTENSION) || FileName.hasSuffix(B25S_EXTENSION)) {
- bool Result = false;
- RenderedImage *pImage = new RenderedImage(FileName, Result);
- if (!Result) {
+ // Load sprite image
+ if (filename.hasSuffix(".png") || filename.hasSuffix(".b25s")) {
+ bool result = false;
+ RenderedImage *pImage = new RenderedImage(filename, result);
+ if (!result) {
delete pImage;
return 0;
}
- BitmapResource *pResource = new BitmapResource(FileName, pImage);
+ BitmapResource *pResource = new BitmapResource(filename, pImage);
if (!pResource->isValid()) {
delete pResource;
return 0;
@@ -297,31 +298,32 @@ Resource *GraphicEngine::loadResource(const Common::String &FileName) {
}
- // Vectorgraphik laden
- if (FileName.hasSuffix(SWF_EXTENSION)) {
- debug(2, "VectorImage: %s", FileName.c_str());
+ // Load vector graphics
+ if (filename.hasSuffix(".swf")) {
+ debug(2, "VectorImage: %s", filename.c_str());
// Pointer auf Package-Manager holen
- PackageManager *pPackage = Kernel::GetInstance()->GetPackage();
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
BS_ASSERT(pPackage);
// Datei laden
byte *pFileData;
- uint FileSize;
- if (!(pFileData = static_cast<byte *>(pPackage->getFile(FileName, &FileSize)))) {
- BS_LOG_ERRORLN("File \"%s\" could not be loaded.", FileName.c_str());
+ uint fileSize;
+ pFileData = pPackage->getFile(filename, &fileSize);
+ if (!pFileData) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
return 0;
}
- bool Result = false;
- VectorImage *pImage = new VectorImage(pFileData, FileSize, Result, FileName);
- if (!Result) {
+ bool result = false;
+ VectorImage *pImage = new VectorImage(pFileData, fileSize, result, filename);
+ if (!result) {
delete pImage;
- delete [] pFileData;
+ delete[] pFileData;
return 0;
}
- BitmapResource *pResource = new BitmapResource(FileName, pImage);
+ BitmapResource *pResource = new BitmapResource(filename, pImage);
if (!pResource->isValid()) {
delete pResource;
delete[] pFileData;
@@ -332,9 +334,9 @@ Resource *GraphicEngine::loadResource(const Common::String &FileName) {
return pResource;
}
- // Animation laden
- if (FileName.hasSuffix(ANI_EXTENSION)) {
- AnimationResource *pResource = new AnimationResource(FileName);
+ // Load animation
+ if (filename.hasSuffix("_ani.xml")) {
+ AnimationResource *pResource = new AnimationResource(filename);
if (pResource->isValid())
return pResource;
else {
@@ -343,10 +345,10 @@ Resource *GraphicEngine::loadResource(const Common::String &FileName) {
}
}
- // Font laden
- if (FileName.hasSuffix(FNT_EXTENSION)) {
- FontResource *pResource = new FontResource(Kernel::GetInstance(), FileName);
- if (pResource->IsValid())
+ // Load font
+ if (filename.hasSuffix("_fnt.xml")) {
+ FontResource *pResource = new FontResource(Kernel::getInstance(), filename);
+ if (pResource->isValid())
return pResource;
else {
delete pResource;
@@ -354,18 +356,18 @@ Resource *GraphicEngine::loadResource(const Common::String &FileName) {
}
}
- BS_LOG_ERRORLN("Service cannot load \"%s\".", FileName.c_str());
+ BS_LOG_ERRORLN("Service cannot load \"%s\".", filename.c_str());
return 0;
}
// -----------------------------------------------------------------------------
-bool GraphicEngine::canLoadResource(const Common::String &FileName) {
- return FileName.hasSuffix(PNG_EXTENSION) ||
- FileName.hasSuffix(ANI_EXTENSION) ||
- FileName.hasSuffix(FNT_EXTENSION) ||
- FileName.hasSuffix(SWF_EXTENSION) ||
- FileName.hasSuffix(B25S_EXTENSION);
+bool GraphicEngine::canLoadResource(const Common::String &filename) {
+ return filename.hasSuffix(".png") ||
+ filename.hasSuffix("_ani.xml") ||
+ filename.hasSuffix("_fnt.xml") ||
+ filename.hasSuffix(".swf") ||
+ filename.hasSuffix(".b25s");
}
@@ -373,57 +375,33 @@ bool GraphicEngine::canLoadResource(const Common::String &FileName) {
// DEBUGGING
// -----------------------------------------------------------------------------
-void GraphicEngine::DrawDebugLine(const Vertex &Start, const Vertex &End, uint Color) {
- m_DebugLines.push_back(DebugLine(Start, End, Color));
+void GraphicEngine::drawDebugLine(const Vertex &start, const Vertex &end, uint color) {
+ _debugLines.push_back(DebugLine(start, end, color));
}
-void GraphicEngine::UpdateLastFrameDuration() {
- // Aktuelle Zeit holen
- uint64_t CurrentTime = Kernel::GetInstance()->GetMicroTicks();
-
- // Verstrichene Zeit seit letztem Frame berechnen und zu große Zeitsprünge ( > 250 msek.) unterbinden
- // (kann vorkommen bei geladenen Spielständen, während des Debuggings oder Hardwareungenauigkeiten)
- m_FrameTimeSamples[m_FrameTimeSampleSlot] = static_cast<uint>(CurrentTime - m_LastTimeStamp);
- if (m_FrameTimeSamples[m_FrameTimeSampleSlot] > 250000) m_FrameTimeSamples[m_FrameTimeSampleSlot] = 250000;
- m_FrameTimeSampleSlot = (m_FrameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
-
- // Die Framezeit wird über mehrere Frames gemittelt um Ausreisser zu eliminieren
- Common::Array<uint>::const_iterator it = m_FrameTimeSamples.begin();
- uint Sum = *it;
- for (it++; it != m_FrameTimeSamples.end(); it++) Sum += *it;
- m_LastFrameDuration = Sum / FRAMETIME_SAMPLE_COUNT;
-
- // _LastTimeStamp auf die Zeit des aktuellen Frames setzen
- m_LastTimeStamp = CurrentTime;
+void GraphicEngine::updateLastFrameDuration() {
+ // Record current time
+ const uint currentTime = Kernel::getInstance()->getMilliTicks();
+
+ // Compute the elapsed time since the last frame and prevent too big ( > 250 msecs) time jumps.
+ // These can occur when loading save states, during debugging or due to hardware inaccuracies.
+ _frameTimeSamples[_frameTimeSampleSlot] = static_cast<uint>(currentTime - _lastTimeStamp);
+ if (_frameTimeSamples[_frameTimeSampleSlot] > 250000)
+ _frameTimeSamples[_frameTimeSampleSlot] = 250000;
+ _frameTimeSampleSlot = (_frameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
+
+ // Compute the average frame duration over multiple frames to eliminate outliers.
+ Common::Array<uint>::const_iterator it = _frameTimeSamples.begin();
+ uint sum = *it;
+ for (it++; it != _frameTimeSamples.end(); it++)
+ sum += *it;
+ _lastFrameDuration = sum * 1000 / FRAMETIME_SAMPLE_COUNT;
+
+ // Update m_LastTimeStamp with the current frame's timestamp
+ _lastTimeStamp = currentTime;
}
-namespace {
-bool DoSaveScreenshot(GraphicEngine &GraphicEngine, const Common::String &Filename) {
- Graphics::Surface *data = GraphicEngine.GetScreenshot();
- if (!data) {
- BS_LOG_ERRORLN("Call to GetScreenshot() failed. Cannot save screenshot.");
- return false;
- }
-
- Common::FSNode f(Filename);
- Common::WriteStream *stream = f.createWriteStream();
- if (!stream) {
- BS_LOG_ERRORLN("Call to GetScreenshot() failed. Cannot save screenshot.");
- return false;
- }
-
- bool result = Screenshot::SaveToFile(data, stream);
- delete stream;
-
- return result;
-}
-}
-
-bool GraphicEngine::SaveScreenshot(const Common::String &Filename) {
- return DoSaveScreenshot(*this, Filename);
-}
-
-bool GraphicEngine::SaveThumbnailScreenshot(const Common::String &Filename) {
+bool GraphicEngine::saveThumbnailScreenshot(const Common::String &filename) {
// Note: In ScumMVM, rather than saivng the thumbnail to a file, we store it in memory
// until needed when creating savegame files
delete _thumbnail;
@@ -431,59 +409,64 @@ bool GraphicEngine::SaveThumbnailScreenshot(const Common::String &Filename) {
return true;
}
-void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint Color) {
- lua_Number Components[4] = {
- (Color >> 16) & 0xff, // Rot
- (Color >> 8) & 0xff, // Grün
- Color & 0xff, // Blau
- Color >> 24, // Alpha
+void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint color) {
+ lua_Number components[4] = {
+ (color >> 16) & 0xff, // Rot
+ (color >> 8) & 0xff, // Grün
+ color & 0xff, // Blau
+ color >> 24, // Alpha
};
lua_newtable(L);
for (uint i = 1; i <= 4; i++) {
lua_pushnumber(L, i);
- lua_pushnumber(L, Components[i - 1]);
+ lua_pushnumber(L, components[i - 1]);
lua_settable(L, -3);
}
}
-uint GraphicEngine::LuaColorToARGBColor(lua_State *L, int StackIndex) {
+uint GraphicEngine::luaColorToARGBColor(lua_State *L, int stackIndex) {
#ifdef DEBUG
int __startStackDepth = lua_gettop(L);
#endif
// Sicherstellen, dass wir wirklich eine Tabelle betrachten
- luaL_checktype(L, StackIndex, LUA_TTABLE);
+ luaL_checktype(L, stackIndex, LUA_TTABLE);
// Größe der Tabelle auslesen
- uint n = luaL_getn(L, StackIndex);
+ uint n = luaL_getn(L, stackIndex);
// RGB oder RGBA Farben werden unterstützt und sonst keine
- if (n != 3 && n != 4) luaL_argcheck(L, 0, StackIndex, "at least 3 of the 4 color components have to be specified");
-
- // Rote Farbkomponente auslesen
- lua_rawgeti(L, StackIndex, 1);
- uint Red = static_cast<uint>(lua_tonumber(L, -1));
- if (!lua_isnumber(L, -1) || Red >= 256) luaL_argcheck(L, 0, StackIndex, "red color component must be an integer between 0 and 255");
+ if (n != 3 && n != 4)
+ luaL_argcheck(L, 0, stackIndex, "at least 3 of the 4 color components have to be specified");
+
+ // Red color component reading
+ lua_rawgeti(L, stackIndex, 1);
+ uint red = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || red >= 256)
+ luaL_argcheck(L, 0, stackIndex, "red color component must be an integer between 0 and 255");
lua_pop(L, 1);
- // Grüne Farbkomponente auslesen
- lua_rawgeti(L, StackIndex, 2);
- uint Green = static_cast<uint>(lua_tonumber(L, -1));
- if (!lua_isnumber(L, -1) || Green >= 256) luaL_argcheck(L, 0, StackIndex, "green color component must be an integer between 0 and 255");
+ // Green color component reading
+ lua_rawgeti(L, stackIndex, 2);
+ uint green = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || green >= 256)
+ luaL_argcheck(L, 0, stackIndex, "green color component must be an integer between 0 and 255");
lua_pop(L, 1);
- // Blaue Farbkomponente auslesen
- lua_rawgeti(L, StackIndex, 3);
- uint Blue = static_cast<uint>(lua_tonumber(L, -1));
- if (!lua_isnumber(L, -1) || Blue >= 256) luaL_argcheck(L, 0, StackIndex, "blue color component must be an integer between 0 and 255");
+ // Blue color component reading
+ lua_rawgeti(L, stackIndex, 3);
+ uint blue = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || blue >= 256)
+ luaL_argcheck(L, 0, stackIndex, "blue color component must be an integer between 0 and 255");
lua_pop(L, 1);
- // Alpha Farbkomponente auslesen
- uint Alpha = 0xff;
+ // Alpha color component reading
+ uint alpha = 0xff;
if (n == 4) {
- lua_rawgeti(L, StackIndex, 4);
- Alpha = static_cast<uint>(lua_tonumber(L, -1));
- if (!lua_isnumber(L, -1) || Alpha >= 256) luaL_argcheck(L, 0, StackIndex, "alpha color component must be an integer between 0 and 255");
+ lua_rawgeti(L, stackIndex, 4);
+ alpha = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || alpha >= 256)
+ luaL_argcheck(L, 0, stackIndex, "alpha color component must be an integer between 0 and 255");
lua_pop(L, 1);
}
@@ -491,11 +474,11 @@ uint GraphicEngine::LuaColorToARGBColor(lua_State *L, int StackIndex) {
BS_ASSERT(__startStackDepth == lua_gettop(L));
#endif
- return (Alpha << 24) | (Red << 16) | (Green << 8) | Blue;
+ return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
bool GraphicEngine::persist(OutputPersistenceBlock &writer) {
- writer.write(m_TimerActive);
+ writer.write(_timerActive);
bool result = _renderObjectManagerPtr->persist(writer);
@@ -503,7 +486,7 @@ bool GraphicEngine::persist(OutputPersistenceBlock &writer) {
}
bool GraphicEngine::unpersist(InputPersistenceBlock &reader) {
- reader.read(m_TimerActive);
+ reader.read(_timerActive);
_renderObjectManagerPtr->unpersist(reader);
return reader.isGood();
diff --git a/engines/sword25/gfx/graphicengine.h b/engines/sword25/gfx/graphicengine.h
index 019f5eec4c..ccfa2ed363 100644
--- a/engines/sword25/gfx/graphicengine.h
+++ b/engines/sword25/gfx/graphicengine.h
@@ -33,7 +33,7 @@
*/
/*
- * BS_GraphicEngine
+ * GraphicEngine
* ----------------
* This the graphics engine interface.
*
@@ -46,10 +46,10 @@
// Includes
#include "common/array.h"
#include "common/rect.h"
+#include "common/ptr.h"
#include "common/str.h"
#include "graphics/surface.h"
#include "sword25/kernel/common.h"
-#include "sword25/kernel/bs_stdint.h"
#include "sword25/kernel/resservice.h"
#include "sword25/kernel/persistable.h"
#include "sword25/gfx/framecounter.h"
@@ -64,44 +64,39 @@ class Panel;
class Screenshot;
class RenderObjectManager;
-// Typen
typedef uint BS_COLOR;
-// Makros
#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
/**
- @brief Dies ist das Graphik-Engine Interface, dass alle Methoden und Klassen enthält, die eine Graphik-Engine implementieren muss.
-
- Hier sind nur wenige Rumpffunktionen realisiert, wie z.B. das Abfragen der Parameter des Ausgabepuffers.
- Die Hauptfunktionen muss eine Implementation dieses Inferfaces stellen.<br>
- Die bisher einzige Implementation ist BS_DDrawGfx.
-*/
-
+ * This is the graphics engine. Unlike the original code, this is not
+ * an interface that needs to be subclassed, but rather already contains
+ * all required functionality.
+ */
class GraphicEngine : public ResourceService, public Persistable {
public:
// Enums
// -----
- // Colour formats
+ // Color formats
//
/**
- * The colour format used by the engine
+ * The color format used by the engine
*/
enum COLOR_FORMATS {
- /// Undefined/unknown colour format
+ /// Undefined/unknown color format
CF_UNKNOWN = 0,
/**
- * 24-bit colour format (R8G8B8)
+ * 24-bit color format (R8G8B8)
*/
CF_RGB24,
/**
- * 32-bit colour format (A8R8G8B8) (little endian)
+ * 32-bit color format (A8R8G8B8) (little endian)
*/
CF_ARGB32,
/**
- 32-bit colour format (A8B8G8R8) (little endian)
+ 32-bit color format (A8B8G8R8) (little endian)
*/
CF_ABGR32
};
@@ -115,15 +110,17 @@ public:
// ---------
/**
- * Initialises the graphics engine and sets the screen mode. Returns true if initialisation failed.
- * Notes: This method should be called immediately after the initialisation of all services.
+ * Initialises the graphics engine and sets the screen mode. Returns
+ * true if initialisation failed.
+ * @note This method should be called immediately after the
+ * initialisation of all services.
*
* @param Height The height of the output buffer in pixels. The default value is 600
* @param BitDepth The bit depth of the desired output buffer in bits. The default value is 16
* @param BackbufferCount The number of back buffers to be created. The default value is 2
* @param Windowed Indicates whether the engine is to run in windowed mode.
*/
- bool Init(int Width = 800, int Height = 600, int BitDepth = 16, int BackbufferCount = 2, bool Windowed = false);
+ bool init(int width = 800, int height = 600, int bitDepth = 16, int backbufferCount = 2, bool windowed = false);
/**
* Begins rendering a new frame.
@@ -132,7 +129,7 @@ public:
* @param UpdateAll Specifies whether the renderer should redraw everything on the next frame.
* This feature can be useful if the renderer with Dirty Rectangles works, but sometimes the client may
*/
- bool StartFrame(bool UpdateAll = false);
+ bool startFrame(bool updateAll = false);
/**
* Ends the rendering of a frame and draws it on the screen.
@@ -140,7 +137,7 @@ public:
* This method must be at the end of the main loop. After this call, no further Render method may be called.
* This should only be called once for a given previous call to #StartFrame.
*/
- bool EndFrame();
+ bool endFrame();
// Debug methods
@@ -152,17 +149,9 @@ public:
* called for every frame.
* @param Start The starting point of the line
* @param End The ending point of the line
- * @param Color The colour of the line. The default is BS_RGB (255,255,255) (White)
+ * @param Color The color of the line. The default is BS_RGB (255,255,255) (White)
*/
- void DrawDebugLine(const Vertex &Start, const Vertex &End, uint Color = BS_RGB(255, 255, 255));
-
- /**
- * Creates a screenshot of the current frame buffer and writes it to a graphic file in PNG format.
- * Returns true if the screenshot was saved successfully.
- * Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
- * @param Filename The filename for the screenshot
- */
- bool SaveScreenshot(const Common::String &Filename);
+ void drawDebugLine(const Vertex &start, const Vertex &end, uint color = BS_RGB(255, 255, 255));
/**
* Creates a thumbnail with the dimensions of 200x125. This will not include the top and bottom of the screen..
@@ -171,7 +160,7 @@ public:
* The frame buffer must have a resolution of 800x600.
* @param Filename The filename for the screenshot
*/
- bool SaveThumbnailScreenshot(const Common::String &Filename);
+ bool saveThumbnailScreenshot(const Common::String &filename);
/**
* Reads the current contents of the frame buffer
@@ -179,37 +168,41 @@ public:
* after a call to EndFrame(), and before the next call to StartFrame().
* @param Width Returns the width of the frame buffer
* @param Height Returns the height of the frame buffer
- * @param Data Returns the raw data of the frame buffer as an array of 32-bit colour values.
+ * @param Data Returns the raw data of the frame buffer as an array of 32-bit color values.
*/
- Graphics::Surface *GetScreenshot();
+ Graphics::Surface *getScreenshot();
- RenderObjectPtr<Panel> GetMainPanel();
+ RenderObjectPtr<Panel> getMainPanel();
/**
* Specifies the time (in microseconds) since the last frame has passed
*/
- int GetLastFrameDurationMicro() {
- if (m_TimerActive) return m_LastFrameDuration;
- else return 0;
+ int getLastFrameDurationMicro() const {
+ if (!_timerActive)
+ return 0;
+ return _lastFrameDuration;
}
/**
* Specifies the time (in microseconds) the previous frame took
*/
- float GetLastFrameDuration() {
- if (m_TimerActive) return static_cast<float>(m_LastFrameDuration) / 1000000.0f;
- else return 0;
+ float getLastFrameDuration() const {
+ if (!_timerActive)
+ return 0;
+ return static_cast<float>(_lastFrameDuration) / 1000000.0f;
}
- void StopMainTimer() {
- m_TimerActive = false;
+ void stopMainTimer() {
+ _timerActive = false;
}
- void ResumeMainTimer() {
- m_TimerActive = true;
+
+ void resumeMainTimer() {
+ _timerActive = true;
}
- float GetSecondaryFrameDuration() {
- return static_cast<float>(m_LastFrameDuration) / 1000000.0f;
+
+ float getSecondaryFrameDuration() const {
+ return static_cast<float>(_lastFrameDuration) / 1000000.0f;
}
// Accessor methods
@@ -217,29 +210,29 @@ public:
/**
* Returns the width of the output buffer in pixels
*/
- int GetDisplayWidth() {
- return m_Width;
+ int getDisplayWidth() const {
+ return _width;
}
/**
* Returns the height of the output buffer in pixels
*/
- int GetDisplayHeight() {
- return m_Height;
+ int getDisplayHeight() const {
+ return _height;
}
/**
* Returns the bounding box of the output buffer: (0, 0, Width, Height)
*/
- Common::Rect &GetDisplayRect() {
- return m_ScreenRect;
+ Common::Rect &getDisplayRect() {
+ return _screenRect;
}
/**
* Returns the bit depth of the output buffer
*/
- int GetBitDepth() {
- return m_BitDepth;
+ int getBitDepth() {
+ return _bitDepth;
}
/**
@@ -247,39 +240,39 @@ public:
* Notes: In windowed mode, this setting has no effect.
* @param Vsync Indicates whether the frame buffer changes are to be synchronised with Vsync.
*/
- void SetVsync(bool Vsync);
+ void setVsync(bool vsync);
/**
* Returns true if V-Sync is on.
* Notes: In windowed mode, this setting has no effect.
*/
- bool GetVsync() const;
+ bool getVsync() const;
/**
* Returns true if the engine is running in Windowed mode.
*/
- bool IsWindowed() {
- return m_Windowed;
+ bool isWindowed() {
+ return _windowed;
}
/**
- * Fills a rectangular area of the frame buffer with a colour.
- * Notes: It is possible to create transparent rectangles by passing a colour with an Alpha value of 255.
+ * Fills a rectangular area of the frame buffer with a color.
+ * Notes: It is possible to create transparent rectangles by passing a color with an Alpha value of 255.
* @param FillRectPtr Pointer to a Common::Rect, which specifies the section of the frame buffer to be filled.
* If the rectangle falls partly off-screen, then it is automatically trimmed.
* If a NULL value is passed, then the entire image is to be filled.
- * @param Color The 32-bit colour with which the area is to be filled. The default is BS_RGB(0, 0, 0) (black)
- @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ * @param Color The 32-bit color with which the area is to be filled. The default is BS_RGB(0, 0, 0) (black)
+ * @note FIf the rectangle is not completely inside the screen, it is automatically clipped.
*/
- bool fill(const Common::Rect *FillRectPtr = 0, uint Color = BS_RGB(0, 0, 0));
+ bool fill(const Common::Rect *fillRectPtr = 0, uint color = BS_RGB(0, 0, 0));
// Debugging Methods
- int GetFPSCount() const {
- return m_FPSCounter.GetFPS();
+ int getFPSCount() const {
+ return _FPSCounter.getFPS();
}
- int GetRepaintedPixels() const {
- return m_RepaintedPixels;
+ int getRepaintedPixels() const {
+ return _repaintedPixels;
}
Graphics::Surface _backSurface;
@@ -288,18 +281,18 @@ public:
Graphics::Surface _frameBuffer;
Graphics::Surface *getFrameBuffer() { return &_frameBuffer; }
- Common::MemoryReadStream *_thumbnail;
- Common::MemoryReadStream *getThumbnail() { return _thumbnail; }
+ Common::SeekableReadStream *_thumbnail;
+ Common::SeekableReadStream *getThumbnail() { return _thumbnail; }
// Access methods
/**
- * Returns the size of a pixel entry in bytes for a particular colour format
- * @param ColorFormat The desired colour format. The parameter must be of type COLOR_FORMATS
- * @return Returns the size of a pixel in bytes. If the colour format is unknown, -1 is returned.
+ * Returns the size of a pixel entry in bytes for a particular color format
+ * @param ColorFormat The desired color format. The parameter must be of type COLOR_FORMATS
+ * @return Returns the size of a pixel in bytes. If the color format is unknown, -1 is returned.
*/
- static int GetPixelSize(GraphicEngine::COLOR_FORMATS ColorFormat) {
- switch (ColorFormat) {
+ static int getPixelSize(GraphicEngine::COLOR_FORMATS colorFormat) {
+ switch (colorFormat) {
case GraphicEngine::CF_ARGB32:
return 4;
default:
@@ -308,16 +301,16 @@ public:
}
/**
- * Calculates the length of an image line in bytes, depending on a given colour format.
- * @param ColorFormat The colour format
+ * Calculates the length of an image line in bytes, depending on a given color format.
+ * @param ColorFormat The color format
* @param Width The width of the line in pixels
- * @return Reflects the length of the line in bytes. If the colour format is
+ * @return Reflects the length of the line in bytes. If the color format is
* unknown, -1 is returned
*/
- static int CalcPitch(GraphicEngine::COLOR_FORMATS ColorFormat, int Width) {
- switch (ColorFormat) {
+ static int calcPitch(GraphicEngine::COLOR_FORMATS colorFormat, int width) {
+ switch (colorFormat) {
case GraphicEngine::CF_ARGB32:
- return Width * 4;
+ return width * 4;
default:
BS_ASSERT(false);
@@ -328,69 +321,70 @@ public:
// Resource-Managing Methods
// --------------------------
- virtual Resource *loadResource(const Common::String &fileName);
- virtual bool canLoadResource(const Common::String &fileName);
+ virtual Resource *loadResource(const Common::String &fileName);
+ virtual bool canLoadResource(const Common::String &fileName);
// Persistence Methods
// -------------------
- virtual bool persist(OutputPersistenceBlock &Writer);
- virtual bool unpersist(InputPersistenceBlock &Reader);
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
- static void ARGBColorToLuaColor(lua_State *L, uint Color);
- static uint LuaColorToARGBColor(lua_State *L, int StackIndex);
+ static void ARGBColorToLuaColor(lua_State *L, uint color);
+ static uint luaColorToARGBColor(lua_State *L, int stackIndex);
protected:
// Display Variables
// -----------------
- int m_Width;
- int m_Height;
- Common::Rect m_ScreenRect;
- int m_BitDepth;
- bool m_Windowed;
+ int _width;
+ int _height;
+ Common::Rect _screenRect;
+ int _bitDepth;
+ bool _windowed;
// Debugging Variables
// -------------------
- Framecounter m_FPSCounter;
+ Framecounter _FPSCounter;
- uint m_RepaintedPixels;
+ uint _repaintedPixels;
/**
* Calculates the time since the last frame beginning has passed.
*/
- void UpdateLastFrameDuration();
+ void updateLastFrameDuration();
private:
- bool RegisterScriptBindings();
+ bool registerScriptBindings();
+ void unregisterScriptBindings();
// LastFrameDuration Variables
// ---------------------------
- uint64 m_LastTimeStamp;
- uint m_LastFrameDuration;
- bool m_TimerActive;
- Common::Array<uint> m_FrameTimeSamples;
- uint m_FrameTimeSampleSlot;
+ uint _lastTimeStamp;
+ uint _lastFrameDuration;
+ bool _timerActive;
+ Common::Array<uint> _frameTimeSamples;
+ uint _frameTimeSampleSlot;
private:
byte *_backBuffer;
- RenderObjectPtr<Panel> m_MainPanelPtr;
+ RenderObjectPtr<Panel> _mainPanelPtr;
- Common::ScopedPtr<RenderObjectManager> _renderObjectManagerPtr;
+ Common::ScopedPtr<RenderObjectManager> _renderObjectManagerPtr;
struct DebugLine {
- DebugLine(const Vertex &_Start, const Vertex &_End, uint _Color) :
- Start(_Start),
- End(_End),
- Color(_Color) {}
+ DebugLine(const Vertex &start, const Vertex &end, uint color) :
+ _start(start),
+ _end(end),
+ _color(color) {}
DebugLine() {}
- Vertex Start;
- Vertex End;
- uint Color;
+ Vertex _start;
+ Vertex _end;
+ uint _color;
};
- Common::Array<DebugLine> m_DebugLines;
+ Common::Array<DebugLine> _debugLines;
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/graphicengine_script.cpp b/engines/sword25/gfx/graphicengine_script.cpp
index d443023436..0814a23871 100644
--- a/engines/sword25/gfx/graphicengine_script.cpp
+++ b/engines/sword25/gfx/graphicengine_script.cpp
@@ -32,13 +32,8 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/kernel/kernel.h"
-#include "sword25/kernel/callbackregistry.h"
#include "sword25/script/script.h"
#include "sword25/script/luabindhelper.h"
#include "sword25/script/luacallback.h"
@@ -57,20 +52,14 @@ namespace Sword25 {
#define BS_LOG_PREFIX "GRAPHICENGINE"
-// -----------------------------------------------------------------------------
-// Callback-Objekte
-// -----------------------------------------------------------------------------
-
-static bool AnimationDeleteCallback(uint Data);
-static bool AnimationActionCallback(uint Data);
-static bool AnimationLoopPointCallback(uint Data);
+static bool animationDeleteCallback(uint Data);
+static bool animationActionCallback(uint Data);
+static bool animationLoopPointCallback(uint Data);
namespace {
-// -------------------------------------------------------------------------
-
class ActionCallback : public LuaCallback {
public:
- ActionCallback(lua_State *L) : LuaCallback(L) {};
+ ActionCallback(lua_State *L) : LuaCallback(L) {}
Common::String Action;
@@ -81,25 +70,10 @@ protected:
}
};
-Common::ScopedPtr<LuaCallback> LoopPointCallbackPtr;
-Common::ScopedPtr<ActionCallback> ActionCallbackPtr;
-
-// -------------------------------------------------------------------------
-
-struct CallbackfunctionRegisterer {
- CallbackfunctionRegisterer() {
- CallbackRegistry::getInstance().registerCallbackFunction("LuaLoopPointCB", (void ( *)(int))AnimationLoopPointCallback);
- CallbackRegistry::getInstance().registerCallbackFunction("LuaActionCB", (void ( *)(int))AnimationActionCallback);
- CallbackRegistry::getInstance().registerCallbackFunction("LuaDeleteCB", (void ( *)(int))AnimationDeleteCallback);
- }
-};
-static CallbackfunctionRegisterer Instance;
+static LuaCallback *loopPointCallbackPtr = 0; // FIXME: should be turned into GraphicEngine member var
+static ActionCallback *actionCallbackPtr = 0; // FIXME: should be turned into GraphicEngine member var
}
-// -----------------------------------------------------------------------------
-// Constants
-// -----------------------------------------------------------------------------
-
// Die Strings werden als #defines definiert um Stringkomposition zur Compilezeit zu ermöglichen.
#define RENDEROBJECT_CLASS_NAME "Gfx.RenderObject"
#define BITMAP_CLASS_NAME "Gfx.Bitmap"
@@ -109,8 +83,6 @@ static CallbackfunctionRegisterer Instance;
#define ANIMATION_TEMPLATE_CLASS_NAME "Gfx.AnimationTemplate"
static const char *GFX_LIBRARY_NAME = "Gfx";
-// -----------------------------------------------------------------------------
-
// Wie luaL_checkudata, nur ohne dass kein Fehler erzeugt wird.
static void *my_checkudata(lua_State *L, int ud, const char *tname) {
int top = lua_gettop(L);
@@ -131,23 +103,19 @@ static void *my_checkudata(lua_State *L, int ud, const char *tname) {
return NULL;
}
-// -----------------------------------------------------------------------------
-
-static void NewUintUserData(lua_State *L, uint Value) {
- void *UserData = lua_newuserdata(L, sizeof(Value));
- memcpy(UserData, &Value, sizeof(Value));
+static void newUintUserData(lua_State *L, uint value) {
+ void *userData = lua_newuserdata(L, sizeof(value));
+ memcpy(userData, &value, sizeof(value));
}
-// -----------------------------------------------------------------------------
-
-static AnimationTemplate *CheckAnimationTemplate(lua_State *L, int idx = 1) {
+static AnimationTemplate *checkAnimationTemplate(lua_State *L, int idx = 1) {
// Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.AnimationTemplate
- uint AnimationTemplateHandle;
- if ((AnimationTemplateHandle = *reinterpret_cast<uint *>(my_checkudata(L, idx, ANIMATION_TEMPLATE_CLASS_NAME))) != 0) {
- AnimationTemplate *AnimationTemplatePtr = AnimationTemplateRegistry::getInstance().resolveHandle(AnimationTemplateHandle);
- if (!AnimationTemplatePtr)
- luaL_error(L, "The animation template with the handle %d does no longer exist.", AnimationTemplateHandle);
- return AnimationTemplatePtr;
+ uint animationTemplateHandle;
+ if ((animationTemplateHandle = *reinterpret_cast<uint *>(my_checkudata(L, idx, ANIMATION_TEMPLATE_CLASS_NAME))) != 0) {
+ AnimationTemplate *animationTemplatePtr = AnimationTemplateRegistry::instance().resolveHandle(animationTemplateHandle);
+ if (!animationTemplatePtr)
+ luaL_error(L, "The animation template with the handle %d does no longer exist.", animationTemplateHandle);
+ return animationTemplatePtr;
} else {
luaL_argcheck(L, 0, idx, "'" ANIMATION_TEMPLATE_CLASS_NAME "' expected");
return 0;
@@ -155,13 +123,11 @@ static AnimationTemplate *CheckAnimationTemplate(lua_State *L, int idx = 1) {
}
-// -----------------------------------------------------------------------------
-
-static int NewAnimationTemplate(lua_State *L) {
- uint AnimationTemplateHandle = AnimationTemplate::create(luaL_checkstring(L, 1));
- AnimationTemplate *AnimationTemplatePtr = AnimationTemplateRegistry::getInstance().resolveHandle(AnimationTemplateHandle);
- if (AnimationTemplatePtr && AnimationTemplatePtr->isValid()) {
- NewUintUserData(L, AnimationTemplateHandle);
+static int newAnimationTemplate(lua_State *L) {
+ uint animationTemplateHandle = AnimationTemplate::create(luaL_checkstring(L, 1));
+ AnimationTemplate *animationTemplatePtr = AnimationTemplateRegistry::instance().resolveHandle(animationTemplateHandle);
+ if (animationTemplatePtr && animationTemplatePtr->isValid()) {
+ newUintUserData(L, animationTemplateHandle);
//luaL_getmetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
LuaBindhelper::getMetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
BS_ASSERT(!lua_isnil(L, -1));
@@ -173,45 +139,37 @@ static int NewAnimationTemplate(lua_State *L) {
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int AT_AddFrame(lua_State *L) {
- AnimationTemplate *pAT = CheckAnimationTemplate(L);
+static int at_addFrame(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
pAT->addFrame(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int AT_SetFrame(lua_State *L) {
- AnimationTemplate *pAT = CheckAnimationTemplate(L);
+static int at_setFrame(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
pAT->setFrame(static_cast<int>(luaL_checknumber(L, 2)), static_cast<int>(luaL_checknumber(L, 3)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static bool AnimationTypeStringToNumber(const char *TypeString, Animation::ANIMATION_TYPES &Result) {
- if (strcmp(TypeString, "jojo") == 0) {
- Result = Animation::AT_JOJO;
+static bool animationTypeStringToNumber(const char *typeString, Animation::ANIMATION_TYPES &result) {
+ if (strcmp(typeString, "jojo") == 0) {
+ result = Animation::AT_JOJO;
return true;
- } else if (strcmp(TypeString, "loop") == 0) {
- Result = Animation::AT_LOOP;
+ } else if (strcmp(typeString, "loop") == 0) {
+ result = Animation::AT_LOOP;
return true;
- } else if (strcmp(TypeString, "oneshot") == 0) {
- Result = Animation::AT_ONESHOT;
+ } else if (strcmp(typeString, "oneshot") == 0) {
+ result = Animation::AT_ONESHOT;
return true;
} else
return false;
}
-// -----------------------------------------------------------------------------
-
-static int AT_SetAnimationType(lua_State *L) {
- AnimationTemplate *pAT = CheckAnimationTemplate(L);
- Animation::ANIMATION_TYPES AnimationType;
- if (AnimationTypeStringToNumber(luaL_checkstring(L, 2), AnimationType)) {
- pAT->setAnimationType(AnimationType);
+static int at_setAnimationType(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
+ Animation::ANIMATION_TYPES animationType;
+ if (animationTypeStringToNumber(luaL_checkstring(L, 2), animationType)) {
+ pAT->setAnimationType(animationType);
} else {
luaL_argcheck(L, 0, 2, "Invalid animation type");
}
@@ -219,68 +177,58 @@ static int AT_SetAnimationType(lua_State *L) {
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int AT_SetFPS(lua_State *L) {
- AnimationTemplate *pAT = CheckAnimationTemplate(L);
+static int at_setFPS(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
pAT->setFPS(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int AT_Finalize(lua_State *L) {
- AnimationTemplate *pAT = CheckAnimationTemplate(L);
+static int at_finalize(lua_State *L) {
+ AnimationTemplate *pAT = checkAnimationTemplate(L);
delete pAT;
return 0;
}
-// -----------------------------------------------------------------------------
-
static const luaL_reg ANIMATION_TEMPLATE_METHODS[] = {
- {"AddFrame", AT_AddFrame},
- {"SetFrame", AT_SetFrame},
- {"SetAnimationType", AT_SetAnimationType},
- {"SetFPS", AT_SetFPS},
- {"__gc", AT_Finalize},
+ {"AddFrame", at_addFrame},
+ {"SetFrame", at_setFrame},
+ {"SetAnimationType", at_setAnimationType},
+ {"SetFPS", at_setFPS},
+ {"__gc", at_finalize},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static GraphicEngine *GetGE() {
- Kernel *pKernel = Kernel::GetInstance();
+static GraphicEngine *getGE() {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- GraphicEngine *pGE = static_cast<GraphicEngine *>(pKernel->GetService("gfx"));
+ GraphicEngine *pGE = pKernel->getGfx();
BS_ASSERT(pGE);
return pGE;
}
-// -----------------------------------------------------------------------------
-
-static int Init(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int init(lua_State *L) {
+ GraphicEngine *pGE = getGE();
switch (lua_gettop(L)) {
case 0:
- lua_pushbooleancpp(L, pGE->Init());
+ lua_pushbooleancpp(L, pGE->init());
break;
case 1:
- lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1))));
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1))));
break;
case 2:
- lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2))));
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2))));
break;
case 3:
- lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
static_cast<int>(luaL_checknumber(L, 3))));
break;
case 4:
- lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4))));
break;
default:
- lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ lua_pushbooleancpp(L, pGE->init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4)),
lua_tobooleancpp(L, 5)));
}
@@ -291,14 +239,14 @@ static int Init(lua_State *L) {
#endif
// Main-Panel zum Gfx-Modul hinzufügen
- RenderObjectPtr<Panel> MainPanelPtr(GetGE()->GetMainPanel());
- BS_ASSERT(MainPanelPtr.isValid());
+ RenderObjectPtr<Panel> mainPanelPtr(getGE()->getMainPanel());
+ BS_ASSERT(mainPanelPtr.isValid());
lua_pushstring(L, GFX_LIBRARY_NAME);
lua_gettable(L, LUA_GLOBALSINDEX);
BS_ASSERT(!lua_isnil(L, -1));
- NewUintUserData(L, MainPanelPtr->getHandle());
+ newUintUserData(L, mainPanelPtr->getHandle());
BS_ASSERT(!lua_isnil(L, -1));
// luaL_getmetatable(L, PANEL_CLASS_NAME);
LuaBindhelper::getMetatable(L, PANEL_CLASS_NAME);
@@ -318,213 +266,177 @@ static int Init(lua_State *L) {
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int StartFrame(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int startFrame(lua_State *L) {
+ GraphicEngine *pGE = getGE();
if (lua_gettop(L) == 0)
- lua_pushbooleancpp(L, pGE->StartFrame());
+ lua_pushbooleancpp(L, pGE->startFrame());
else
- lua_pushbooleancpp(L, pGE->StartFrame(lua_tobooleancpp(L, 1)));
+ lua_pushbooleancpp(L, pGE->startFrame(lua_tobooleancpp(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
+static int endFrame(lua_State *L) {
+ GraphicEngine *pGE = getGE();
-static int EndFrame(lua_State *L) {
- GraphicEngine *pGE = GetGE();
-
- lua_pushbooleancpp(L, pGE->EndFrame());
+ lua_pushbooleancpp(L, pGE->endFrame());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int DrawDebugLine(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int drawDebugLine(lua_State *L) {
+ GraphicEngine *pGE = getGE();
- Vertex Start;
- Vertex End;
- Vertex::luaVertexToVertex(L, 1, Start);
- Vertex::luaVertexToVertex(L, 2, End);
- pGE->DrawDebugLine(Start, End, GraphicEngine::LuaColorToARGBColor(L, 3));
+ Vertex start;
+ Vertex end;
+ Vertex::luaVertexToVertex(L, 1, start);
+ Vertex::luaVertexToVertex(L, 2, end);
+ pGE->drawDebugLine(start, end, GraphicEngine::luaColorToARGBColor(L, 3));
return 0;
}
-// -----------------------------------------------------------------------------
+static int getDisplayWidth(lua_State *L) {
+ GraphicEngine *pGE = getGE();
-static int GetDisplayWidth(lua_State *L) {
- GraphicEngine *pGE = GetGE();
-
- lua_pushnumber(L, pGE->GetDisplayWidth());
+ lua_pushnumber(L, pGE->getDisplayWidth());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetDisplayHeight(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int getDisplayHeight(lua_State *L) {
+ GraphicEngine *pGE = getGE();
- lua_pushnumber(L, pGE->GetDisplayHeight());
+ lua_pushnumber(L, pGE->getDisplayHeight());
return 1;
}
-// -----------------------------------------------------------------------------
+static int getBitDepth(lua_State *L) {
+ GraphicEngine *pGE = getGE();
-static int GetBitDepth(lua_State *L) {
- GraphicEngine *pGE = GetGE();
-
- lua_pushnumber(L, pGE->GetBitDepth());
+ lua_pushnumber(L, pGE->getBitDepth());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetVsync(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int setVsync(lua_State *L) {
+ GraphicEngine *pGE = getGE();
- pGE->SetVsync(lua_tobooleancpp(L, 1));
+ pGE->setVsync(lua_tobooleancpp(L, 1));
return 0;
}
-// -----------------------------------------------------------------------------
+static int isVsync(lua_State *L) {
+ GraphicEngine *pGE = getGE();
-static int IsVsync(lua_State *L) {
- GraphicEngine *pGE = GetGE();
-
- lua_pushbooleancpp(L, pGE->GetVsync());
+ lua_pushbooleancpp(L, pGE->getVsync());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int IsWindowed(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int isWindowed(lua_State *L) {
+ GraphicEngine *pGE = getGE();
- lua_pushbooleancpp(L, pGE->IsWindowed());
+ lua_pushbooleancpp(L, pGE->isWindowed());
return 1;
}
-// -----------------------------------------------------------------------------
+static int getFPSCount(lua_State *L) {
+ GraphicEngine *pGE = getGE();
-static int GetFPSCount(lua_State *L) {
- GraphicEngine *pGE = GetGE();
-
- lua_pushnumber(L, pGE->GetFPSCount());
+ lua_pushnumber(L, pGE->getFPSCount());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetLastFrameDuration(lua_State *L) {
- GraphicEngine *pGE = GetGE();
+static int getLastFrameDuration(lua_State *L) {
+ GraphicEngine *pGE = getGE();
- lua_pushnumber(L, pGE->GetLastFrameDuration());
+ lua_pushnumber(L, pGE->getLastFrameDuration());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int StopMainTimer(lua_State *L) {
- GraphicEngine *pGE = GetGE();
- pGE->StopMainTimer();
+static int stopMainTimer(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ pGE->stopMainTimer();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int ResumeMainTimer(lua_State *L) {
- GraphicEngine *pGE = GetGE();
- pGE->ResumeMainTimer();
+static int resumeMainTimer(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ pGE->resumeMainTimer();
return 0;
}
-// -----------------------------------------------------------------------------
+static int getSecondaryFrameDuration(lua_State *L) {
+ GraphicEngine *pGE = getGE();
-static int GetSecondaryFrameDuration(lua_State *L) {
- GraphicEngine *pGE = GetGE();
-
- lua_pushnumber(L, pGE->GetSecondaryFrameDuration());
+ lua_pushnumber(L, pGE->getSecondaryFrameDuration());
return 1;
}
-// -----------------------------------------------------------------------------
+static int saveScreenshot(lua_State *L) {
+ // This is used by system/debug.lua only. We do not implement this; support
+ // for taking screenshots is a backend feature.
+ lua_pushbooleancpp(L, false);
-static int SaveScreenshot(lua_State *L) {
- GraphicEngine *pGE = GetGE();
- lua_pushbooleancpp(L, pGE->SaveScreenshot(luaL_checkstring(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SaveThumbnailScreenshot(lua_State *L) {
- GraphicEngine *pGE = GetGE();
- lua_pushbooleancpp(L, pGE->SaveThumbnailScreenshot(luaL_checkstring(L, 1)));
+static int saveThumbnailScreenshot(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ lua_pushbooleancpp(L, pGE->saveThumbnailScreenshot(luaL_checkstring(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetRepaintedPixels(lua_State *L) {
- GraphicEngine *pGE = GetGE();
- lua_pushnumber(L, static_cast<lua_Number>(pGE->GetRepaintedPixels()));
+static int getRepaintedPixels(lua_State *L) {
+ GraphicEngine *pGE = getGE();
+ lua_pushnumber(L, static_cast<lua_Number>(pGE->getRepaintedPixels()));
return 1;
}
-// -----------------------------------------------------------------------------
-
static const luaL_reg GFX_FUNCTIONS[] = {
- {"Init", Init},
- {"StartFrame", StartFrame},
- {"EndFrame", EndFrame},
- {"DrawDebugLine", DrawDebugLine},
- {"SetVsync", SetVsync},
- {"GetDisplayWidth", GetDisplayWidth},
- {"GetDisplayHeight", GetDisplayHeight},
- {"GetBitDepth", GetBitDepth},
- {"IsVsync", IsVsync},
- {"IsWindowed", IsWindowed},
- {"GetFPSCount", GetFPSCount},
- {"GetLastFrameDuration", GetLastFrameDuration},
- {"StopMainTimer", StopMainTimer},
- {"ResumeMainTimer", ResumeMainTimer},
- {"GetSecondaryFrameDuration", GetSecondaryFrameDuration},
- {"SaveScreenshot", SaveScreenshot},
- {"NewAnimationTemplate", NewAnimationTemplate},
- {"GetRepaintedPixels", GetRepaintedPixels},
- {"SaveThumbnailScreenshot", SaveThumbnailScreenshot},
+ {"Init", init},
+ {"StartFrame", startFrame},
+ {"EndFrame", endFrame},
+ {"DrawDebugLine", drawDebugLine},
+ {"SetVsync", setVsync},
+ {"GetDisplayWidth", getDisplayWidth},
+ {"GetDisplayHeight", getDisplayHeight},
+ {"GetBitDepth", getBitDepth},
+ {"IsVsync", isVsync},
+ {"IsWindowed", isWindowed},
+ {"GetFPSCount", getFPSCount},
+ {"GetLastFrameDuration", getLastFrameDuration},
+ {"StopMainTimer", stopMainTimer},
+ {"ResumeMainTimer", resumeMainTimer},
+ {"GetSecondaryFrameDuration", getSecondaryFrameDuration},
+ {"SaveScreenshot", saveScreenshot},
+ {"NewAnimationTemplate", newAnimationTemplate},
+ {"GetRepaintedPixels", getRepaintedPixels},
+ {"SaveThumbnailScreenshot", saveThumbnailScreenshot},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static RenderObjectPtr<RenderObject> CheckRenderObject(lua_State *L, bool ErrorIfRemoved = true) {
+static RenderObjectPtr<RenderObject> checkRenderObject(lua_State *L, bool errorIfRemoved = true) {
// Der erste Parameter muss vom Typ userdata sein und die Metatable einer Klasse haben, die von Gfx.RenderObject "erbt".
- uint *UserDataPtr;
- if ((UserDataPtr = (uint *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0 ||
- (UserDataPtr = (uint *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0 ||
- (UserDataPtr = (uint *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0 ||
- (UserDataPtr = (uint *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
- RenderObjectPtr<RenderObject> ROPtr(* UserDataPtr);
- if (ROPtr.isValid())
- return ROPtr;
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0 ||
+ (userDataPtr = (uint *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0 ||
+ (userDataPtr = (uint *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0 ||
+ (userDataPtr = (uint *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid())
+ return roPtr;
else {
- if (ErrorIfRemoved)
- luaL_error(L, "The renderobject with the handle %d does no longer exist.", * UserDataPtr);
+ if (errorIfRemoved)
+ luaL_error(L, "The renderobject with the handle %d does no longer exist.", *userDataPtr);
}
} else {
luaL_argcheck(L, 0, 1, "'" RENDEROBJECT_CLASS_NAME "' expected");
@@ -533,143 +445,115 @@ static RenderObjectPtr<RenderObject> CheckRenderObject(lua_State *L, bool ErrorI
return RenderObjectPtr<RenderObject>();
}
-// -----------------------------------------------------------------------------
-
-static int RO_SetPos(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- Vertex Pos;
- Vertex::luaVertexToVertex(L, 2, Pos);
- ROPtr->setPos(Pos.x, Pos.y);
+static int ro_setPos(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ Vertex pos;
+ Vertex::luaVertexToVertex(L, 2, pos);
+ roPtr->setPos(pos.x, pos.y);
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int RO_SetX(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- ROPtr->setX(static_cast<int>(luaL_checknumber(L, 2)));
+static int ro_setX(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setX(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int RO_SetY(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- ROPtr->setY(static_cast<int>(luaL_checknumber(L, 2)));
+static int ro_setY(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setY(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int RO_SetZ(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- ROPtr->setZ(static_cast<int>(luaL_checknumber(L, 2)));
+static int ro_setZ(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setZ(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int RO_SetVisible(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- ROPtr->setVisible(lua_tobooleancpp(L, 2));
+static int ro_setVisible(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr->setVisible(lua_tobooleancpp(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetX(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getX());
+static int ro_getX(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getX());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetY(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getY());
+static int ro_getY(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getY());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetZ(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getZ());
+static int ro_getZ(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getZ());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetAbsoluteX(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getAbsoluteX());
+static int ro_getAbsoluteX(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getAbsoluteX());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetAbsoluteY(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getAbsoluteY());
+static int ro_getAbsoluteY(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getAbsoluteY());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetWidth(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getWidth());
+static int ro_getWidth(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getWidth());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_GetHeight(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushnumber(L, ROPtr->getHeight());
+static int ro_getHeight(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushnumber(L, roPtr->getHeight());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_IsVisible(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- lua_pushbooleancpp(L, ROPtr->isVisible());
+static int ro_isVisible(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ lua_pushbooleancpp(L, roPtr->isVisible());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_AddPanel(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- RenderObjectPtr<Panel> PanelPtr = ROPtr->addPanel(static_cast<int>(luaL_checknumber(L, 2)),
+static int ro_addPanel(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ RenderObjectPtr<Panel> panelPtr = roPtr->addPanel(static_cast<int>(luaL_checknumber(L, 2)),
static_cast<int>(luaL_checknumber(L, 3)),
- GraphicEngine::LuaColorToARGBColor(L, 4));
- if (PanelPtr.isValid()) {
- NewUintUserData(L, PanelPtr->getHandle());
+ GraphicEngine::luaColorToARGBColor(L, 4));
+ if (panelPtr.isValid()) {
+ newUintUserData(L, panelPtr->getHandle());
// luaL_getmetatable(L, PANEL_CLASS_NAME);
LuaBindhelper::getMetatable(L, PANEL_CLASS_NAME);
BS_ASSERT(!lua_isnil(L, -1));
@@ -680,14 +564,12 @@ static int RO_AddPanel(lua_State *L) {
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_AddBitmap(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- RenderObjectPtr<Bitmap> BitmaPtr = ROPtr->addBitmap(luaL_checkstring(L, 2));
- if (BitmaPtr.isValid()) {
- NewUintUserData(L, BitmaPtr->getHandle());
+static int ro_addBitmap(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ RenderObjectPtr<Bitmap> bitmaPtr = roPtr->addBitmap(luaL_checkstring(L, 2));
+ if (bitmaPtr.isValid()) {
+ newUintUserData(L, bitmaPtr->getHandle());
// luaL_getmetatable(L, BITMAP_CLASS_NAME);
LuaBindhelper::getMetatable(L, BITMAP_CLASS_NAME);
BS_ASSERT(!lua_isnil(L, -1));
@@ -698,18 +580,18 @@ static int RO_AddBitmap(lua_State *L) {
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_AddText(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
+static int ro_addText(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
- RenderObjectPtr<Text> TextPtr;
- if (lua_gettop(L) >= 3) TextPtr = ROPtr->addText(luaL_checkstring(L, 2), luaL_checkstring(L, 3));
- else TextPtr = ROPtr->addText(luaL_checkstring(L, 2));
+ RenderObjectPtr<Text> textPtr;
+ if (lua_gettop(L) >= 3)
+ textPtr = roPtr->addText(luaL_checkstring(L, 2), luaL_checkstring(L, 3));
+ else
+ textPtr = roPtr->addText(luaL_checkstring(L, 2));
- if (TextPtr.isValid()) {
- NewUintUserData(L, TextPtr->getHandle());
+ if (textPtr.isValid()) {
+ newUintUserData(L, textPtr->getHandle());
// luaL_getmetatable(L, TEXT_CLASS_NAME);
LuaBindhelper::getMetatable(L, TEXT_CLASS_NAME);
BS_ASSERT(!lua_isnil(L, -1));
@@ -720,69 +602,67 @@ static int RO_AddText(lua_State *L) {
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int RO_AddAnimation(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
+static int ro_addAnimation(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
- RenderObjectPtr<Animation> AnimationPtr;
+ RenderObjectPtr<Animation> animationPtr;
if (lua_type(L, 2) == LUA_TUSERDATA)
- AnimationPtr = ROPtr->addAnimation(*CheckAnimationTemplate(L, 2));
+ animationPtr = roPtr->addAnimation(*checkAnimationTemplate(L, 2));
else
- AnimationPtr = ROPtr->addAnimation(luaL_checkstring(L, 2));
+ animationPtr = roPtr->addAnimation(luaL_checkstring(L, 2));
- if (AnimationPtr.isValid()) {
- NewUintUserData(L, AnimationPtr->getHandle());
+ if (animationPtr.isValid()) {
+ newUintUserData(L, animationPtr->getHandle());
// luaL_getmetatable(L, ANIMATION_CLASS_NAME);
LuaBindhelper::getMetatable(L, ANIMATION_CLASS_NAME);
BS_ASSERT(!lua_isnil(L, -1));
lua_setmetatable(L, -2);
// Alle Animationscallbacks registrieren.
- AnimationPtr->registerDeleteCallback(AnimationDeleteCallback, AnimationPtr->getHandle());
- AnimationPtr->registerLoopPointCallback(AnimationLoopPointCallback, AnimationPtr->getHandle());
- AnimationPtr->registerActionCallback(AnimationActionCallback, AnimationPtr->getHandle());
+ animationPtr->setCallbacks();
} else
lua_pushnil(L);
return 1;
}
-// -----------------------------------------------------------------------------
+void Animation::setCallbacks() {
+ _actionCallback = animationActionCallback;
+ _loopPointCallback = animationLoopPointCallback;
+ _deleteCallback = animationDeleteCallback;
+}
static const luaL_reg RENDEROBJECT_METHODS[] = {
- {"AddAnimation", RO_AddAnimation},
- {"AddText", RO_AddText},
- {"AddBitmap", RO_AddBitmap},
- {"AddPanel", RO_AddPanel},
- {"SetPos", RO_SetPos},
- {"SetX", RO_SetX},
- {"SetY", RO_SetY},
- {"SetZ", RO_SetZ},
- {"SetVisible", RO_SetVisible},
- {"GetX", RO_GetX},
- {"GetY", RO_GetY},
- {"GetZ", RO_GetZ},
- {"GetAbsoluteX", RO_GetAbsoluteX},
- {"GetAbsoluteY", RO_GetAbsoluteY},
- {"GetWidth", RO_GetWidth},
- {"GetHeight", RO_GetHeight},
- {"IsVisible", RO_IsVisible},
+ {"AddAnimation", ro_addAnimation},
+ {"AddText", ro_addText},
+ {"AddBitmap", ro_addBitmap},
+ {"AddPanel", ro_addPanel},
+ {"SetPos", ro_setPos},
+ {"SetX", ro_setX},
+ {"SetY", ro_setY},
+ {"SetZ", ro_setZ},
+ {"SetVisible", ro_setVisible},
+ {"GetX", ro_getX},
+ {"GetY", ro_getY},
+ {"GetZ", ro_getZ},
+ {"GetAbsoluteX", ro_getAbsoluteX},
+ {"GetAbsoluteY", ro_getAbsoluteY},
+ {"GetWidth", ro_getWidth},
+ {"GetHeight", ro_getHeight},
+ {"IsVisible", ro_isVisible},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static RenderObjectPtr<Panel> CheckPanel(lua_State *L) {
+static RenderObjectPtr<Panel> checkPanel(lua_State *L) {
// Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Panel
- uint *UserDataPtr;
- if ((UserDataPtr = (uint *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0) {
- RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
- if (ROPtr.isValid()) {
- return ROPtr->toPanel();
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid()) {
+ return roPtr->toPanel();
} else
- luaL_error(L, "The panel with the handle %d does no longer exist.", *UserDataPtr);
+ luaL_error(L, "The panel with the handle %d does no longer exist.", *userDataPtr);
} else {
luaL_argcheck(L, 0, 1, "'" PANEL_CLASS_NAME "' expected");
}
@@ -790,54 +670,44 @@ static RenderObjectPtr<Panel> CheckPanel(lua_State *L) {
return RenderObjectPtr<Panel>();
}
-// -----------------------------------------------------------------------------
-
-static int P_GetColor(lua_State *L) {
- RenderObjectPtr<Panel> PanelPtr = CheckPanel(L);
+static int p_getColor(lua_State *L) {
+ RenderObjectPtr<Panel> PanelPtr = checkPanel(L);
BS_ASSERT(PanelPtr.isValid());
GraphicEngine::ARGBColorToLuaColor(L, PanelPtr->getColor());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int P_SetColor(lua_State *L) {
- RenderObjectPtr<Panel> PanelPtr = CheckPanel(L);
+static int p_setColor(lua_State *L) {
+ RenderObjectPtr<Panel> PanelPtr = checkPanel(L);
BS_ASSERT(PanelPtr.isValid());
- PanelPtr->setColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+ PanelPtr->setColor(GraphicEngine::luaColorToARGBColor(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int P_Remove(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- ROPtr.erase();
+static int p_remove(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr.erase();
return 0;
}
-// -----------------------------------------------------------------------------
-
static const luaL_reg PANEL_METHODS[] = {
- {"GetColor", P_GetColor},
- {"SetColor", P_SetColor},
- {"Remove", P_Remove},
+ {"GetColor", p_getColor},
+ {"SetColor", p_setColor},
+ {"Remove", p_remove},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static RenderObjectPtr<Bitmap> CheckBitmap(lua_State *L) {
+static RenderObjectPtr<Bitmap> checkBitmap(lua_State *L) {
// Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Bitmap
- uint *UserDataPtr;
- if ((UserDataPtr = (uint *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0) {
- RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
- if (ROPtr.isValid()) {
- return ROPtr->toBitmap();
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid()) {
+ return roPtr->toBitmap();
} else
- luaL_error(L, "The bitmap with the handle %d does no longer exist.", *UserDataPtr);
+ luaL_error(L, "The bitmap with the handle %d does no longer exist.", *userDataPtr);
} else {
luaL_argcheck(L, 0, 1, "'" BITMAP_CLASS_NAME "' expected");
}
@@ -845,204 +715,165 @@ static RenderObjectPtr<Bitmap> CheckBitmap(lua_State *L) {
return RenderObjectPtr<Bitmap>();
}
-// -----------------------------------------------------------------------------
-
-static int B_SetAlpha(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setAlpha(static_cast<uint>(luaL_checknumber(L, 2)));
+static int b_setAlpha(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setAlpha(static_cast<uint>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_SetTintColor(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setModulationColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+static int b_setTintColor(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setModulationColor(GraphicEngine::luaColorToARGBColor(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_SetScaleFactor(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+static int b_setScaleFactor(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_SetScaleFactorX(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+static int b_setScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_SetScaleFactorY(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+static int b_setScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_SetFlipH(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setFlipH(lua_tobooleancpp(L, 2));
+static int b_setFlipH(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setFlipH(lua_tobooleancpp(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_SetFlipV(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- BitmapPtr->setFlipV(lua_tobooleancpp(L, 2));
+static int b_setFlipV(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ bitmapPtr->setFlipV(lua_tobooleancpp(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int B_GetAlpha(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushnumber(L, BitmapPtr->getAlpha());
+static int b_getAlpha(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushnumber(L, bitmapPtr->getAlpha());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_GetTintColor(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- GraphicEngine::ARGBColorToLuaColor(L, BitmapPtr->getModulationColor());
+static int b_getTintColor(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ GraphicEngine::ARGBColorToLuaColor(L, bitmapPtr->getModulationColor());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_GetScaleFactorX(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushnumber(L, BitmapPtr->getScaleFactorX());
+static int b_getScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushnumber(L, bitmapPtr->getScaleFactorX());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_GetScaleFactorY(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushnumber(L, BitmapPtr->getScaleFactorY());
+static int b_getScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushnumber(L, bitmapPtr->getScaleFactorY());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_IsFlipH(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushbooleancpp(L, BitmapPtr->isFlipH());
+static int b_isFlipH(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isFlipH());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_IsFlipV(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushbooleancpp(L, BitmapPtr->isFlipV());
+static int b_isFlipV(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isFlipV());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_GetPixel(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
+static int b_getPixel(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
Vertex Pos;
Vertex::luaVertexToVertex(L, 2, Pos);
- GraphicEngine::ARGBColorToLuaColor(L, BitmapPtr->getPixel(Pos.x, Pos.y));
+ GraphicEngine::ARGBColorToLuaColor(L, bitmapPtr->getPixel(Pos.x, Pos.y));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_IsScalingAllowed(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushbooleancpp(L, BitmapPtr->isScalingAllowed());
+static int b_isScalingAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isScalingAllowed());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_IsAlphaAllowed(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushbooleancpp(L, BitmapPtr->isAlphaAllowed());
+static int b_isAlphaAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isAlphaAllowed());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int B_IsTintingAllowed(lua_State *L) {
- RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
- BS_ASSERT(BitmapPtr.isValid());
- lua_pushbooleancpp(L, BitmapPtr->isColorModulationAllowed());
+static int b_isTintingAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> bitmapPtr = checkBitmap(L);
+ BS_ASSERT(bitmapPtr.isValid());
+ lua_pushbooleancpp(L, bitmapPtr->isColorModulationAllowed());
return 1;
}
-// -----------------------------------------------------------------------------
-static int B_Remove(lua_State *L) {
- RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
- BS_ASSERT(ROPtr.isValid());
- ROPtr.erase();
+static int b_remove(lua_State *L) {
+ RenderObjectPtr<RenderObject> roPtr = checkRenderObject(L);
+ BS_ASSERT(roPtr.isValid());
+ roPtr.erase();
return 0;
}
-// -----------------------------------------------------------------------------
-
static const luaL_reg BITMAP_METHODS[] = {
- {"SetAlpha", B_SetAlpha},
- {"SetTintColor", B_SetTintColor},
- {"SetScaleFactor", B_SetScaleFactor},
- {"SetScaleFactorX", B_SetScaleFactorX},
- {"SetScaleFactorY", B_SetScaleFactorY},
- {"SetFlipH", B_SetFlipH},
- {"SetFlipV", B_SetFlipV},
- {"GetAlpha", B_GetAlpha},
- {"GetTintColor", B_GetTintColor},
- {"GetScaleFactorX", B_GetScaleFactorX},
- {"GetScaleFactorY", B_GetScaleFactorY},
- {"IsFlipH", B_IsFlipH},
- {"IsFlipV", B_IsFlipV},
- {"GetPixel", B_GetPixel},
- {"IsScalingAllowed", B_IsScalingAllowed},
- {"IsAlphaAllowed", B_IsAlphaAllowed},
- {"IsTintingAllowed", B_IsTintingAllowed},
- {"Remove", B_Remove},
+ {"SetAlpha", b_setAlpha},
+ {"SetTintColor", b_setTintColor},
+ {"SetScaleFactor", b_setScaleFactor},
+ {"SetScaleFactorX", b_setScaleFactorX},
+ {"SetScaleFactorY", b_setScaleFactorY},
+ {"SetFlipH", b_setFlipH},
+ {"SetFlipV", b_setFlipV},
+ {"GetAlpha", b_getAlpha},
+ {"GetTintColor", b_getTintColor},
+ {"GetScaleFactorX", b_getScaleFactorX},
+ {"GetScaleFactorY", b_getScaleFactorY},
+ {"IsFlipH", b_isFlipH},
+ {"IsFlipV", b_isFlipV},
+ {"GetPixel", b_getPixel},
+ {"IsScalingAllowed", b_isScalingAllowed},
+ {"IsAlphaAllowed", b_isAlphaAllowed},
+ {"IsTintingAllowed", b_isTintingAllowed},
+ {"Remove", b_remove},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static RenderObjectPtr<Animation> CheckAnimation(lua_State *L) {
+static RenderObjectPtr<Animation> checkAnimation(lua_State *L) {
// Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Animation
- uint *UserDataPtr;
- if ((UserDataPtr = (uint *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0) {
- RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
- if (ROPtr.isValid())
- return ROPtr->toAnimation();
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid())
+ return roPtr->toAnimation();
else {
- luaL_error(L, "The animation with the handle %d does no longer exist.", *UserDataPtr);
+ luaL_error(L, "The animation with the handle %d does no longer exist.", *userDataPtr);
}
} else {
luaL_argcheck(L, 0, 1, "'" ANIMATION_CLASS_NAME "' expected");
@@ -1051,110 +882,87 @@ static RenderObjectPtr<Animation> CheckAnimation(lua_State *L) {
return RenderObjectPtr<Animation>();
}
-// -----------------------------------------------------------------------------
-
-static int A_Play(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->play();
+static int a_play(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->play();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_Pause(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->pause();
+static int a_pause(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->pause();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_Stop(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->stop();
+static int a_stop(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->stop();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_SetFrame(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->setFrame(static_cast<uint>(luaL_checknumber(L, 2)));
+static int a_setFrame(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setFrame(static_cast<uint>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_SetAlpha(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+static int a_setAlpha(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-static int A_SetTintColor(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->setModulationColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+static int a_setTintColor(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setModulationColor(GraphicEngine::luaColorToARGBColor(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_SetScaleFactor(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+static int a_setScaleFactor(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_SetScaleFactorX(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+static int a_setScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_SetScaleFactorY(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+static int a_setScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_GetScaleFactorX(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushnumber(L, AnimationPtr->getScaleFactorX());
+static int a_getScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getScaleFactorX());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_GetScaleFactorY(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushnumber(L, AnimationPtr->getScaleFactorY());
+static int a_getScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getScaleFactorY());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_GetAnimationType(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- switch (AnimationPtr->getAnimationType()) {
+static int a_getAnimationType(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ switch (animationPtr->getAnimationType()) {
case Animation::AT_JOJO:
lua_pushstring(L, "jojo");
break;
@@ -1170,213 +978,176 @@ static int A_GetAnimationType(lua_State *L) {
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_GetFPS(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushnumber(L, AnimationPtr->getFPS());
+static int a_getFPS(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getFPS());
return 1;
}
-
-// -----------------------------------------------------------------------------
-
-static int A_GetFrameCount(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushnumber(L, AnimationPtr->getFrameCount());
+static int a_getFrameCount(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getFrameCount());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_IsScalingAllowed(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushbooleancpp(L, AnimationPtr->isScalingAllowed());
+static int a_isScalingAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isScalingAllowed());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_IsAlphaAllowed(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushbooleancpp(L, AnimationPtr->isAlphaAllowed());
+static int a_isAlphaAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isAlphaAllowed());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_IsTintingAllowed(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushbooleancpp(L, AnimationPtr->isColorModulationAllowed());
+static int a_isTintingAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isColorModulationAllowed());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_GetCurrentFrame(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushnumber(L, AnimationPtr->getCurrentFrame());
+static int a_getCurrentFrame(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushnumber(L, animationPtr->getCurrentFrame());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_GetCurrentAction(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushstring(L, AnimationPtr->getCurrentAction().c_str());
+static int a_getCurrentAction(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushstring(L, animationPtr->getCurrentAction().c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int A_IsPlaying(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- lua_pushbooleancpp(L, AnimationPtr->isRunning());
+static int a_isPlaying(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ lua_pushbooleancpp(L, animationPtr->isRunning());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static bool AnimationLoopPointCallback(uint Handle) {
- lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
- LoopPointCallbackPtr->invokeCallbackFunctions(L, Handle);
+static bool animationLoopPointCallback(uint handle) {
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ loopPointCallbackPtr->invokeCallbackFunctions(L, handle);
return true;
}
-// -----------------------------------------------------------------------------
-
-static int A_RegisterLoopPointCallback(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
+static int a_registerLoopPointCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_pushvalue(L, 2);
- LoopPointCallbackPtr->registerCallbackFunction(L, AnimationPtr->getHandle());
+ loopPointCallbackPtr->registerCallbackFunction(L, animationPtr->getHandle());
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_UnregisterLoopPointCallback(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
+static int a_unregisterLoopPointCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_pushvalue(L, 2);
- LoopPointCallbackPtr->unregisterCallbackFunction(L, AnimationPtr->getHandle());
+ loopPointCallbackPtr->unregisterCallbackFunction(L, animationPtr->getHandle());
return 0;
}
-// -----------------------------------------------------------------------------
-
-static bool AnimationActionCallback(uint Handle) {
- RenderObjectPtr<Animation> AnimationPtr(Handle);
- if (AnimationPtr.isValid()) {
- ActionCallbackPtr->Action = AnimationPtr->getCurrentAction();
- lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
- ActionCallbackPtr->invokeCallbackFunctions(L, AnimationPtr->getHandle());
+static bool animationActionCallback(uint Handle) {
+ RenderObjectPtr<Animation> animationPtr(Handle);
+ if (animationPtr.isValid()) {
+ actionCallbackPtr->Action = animationPtr->getCurrentAction();
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ actionCallbackPtr->invokeCallbackFunctions(L, animationPtr->getHandle());
}
return true;
}
-// -----------------------------------------------------------------------------
-
-static int A_RegisterActionCallback(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
+static int a_registerActionCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_pushvalue(L, 2);
- ActionCallbackPtr->registerCallbackFunction(L, AnimationPtr->getHandle());
+ actionCallbackPtr->registerCallbackFunction(L, animationPtr->getHandle());
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int A_UnregisterActionCallback(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
+static int a_unregisterActionCallback(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_pushvalue(L, 2);
- ActionCallbackPtr->unregisterCallbackFunction(L, AnimationPtr->getHandle());
+ actionCallbackPtr->unregisterCallbackFunction(L, animationPtr->getHandle());
return 0;
}
-// -----------------------------------------------------------------------------
-
-static bool AnimationDeleteCallback(uint Handle) {
- lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
- LoopPointCallbackPtr->removeAllObjectCallbacks(L, Handle);
+static bool animationDeleteCallback(uint Handle) {
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ loopPointCallbackPtr->removeAllObjectCallbacks(L, Handle);
return true;
}
-// -----------------------------------------------------------------------------
-
-static int A_Remove(lua_State *L) {
- RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
- BS_ASSERT(AnimationPtr.isValid());
- AnimationPtr.erase();
+static int a_remove(lua_State *L) {
+ RenderObjectPtr<Animation> animationPtr = checkAnimation(L);
+ BS_ASSERT(animationPtr.isValid());
+ animationPtr.erase();
return 0;
}
-// -----------------------------------------------------------------------------
-
static const luaL_reg ANIMATION_METHODS[] = {
- {"Play", A_Play},
- {"Pause", A_Pause},
- {"Stop", A_Stop},
- {"SetFrame", A_SetFrame},
- {"SetAlpha", A_SetAlpha},
- {"SetTintColor", A_SetTintColor},
- {"SetScaleFactor", A_SetScaleFactor},
- {"SetScaleFactorX", A_SetScaleFactorX},
- {"SetScaleFactorY", A_SetScaleFactorY},
- {"GetScaleFactorX", A_GetScaleFactorX},
- {"GetScaleFactorY", A_GetScaleFactorY},
- {"GetAnimationType", A_GetAnimationType},
- {"GetFPS", A_GetFPS},
- {"GetFrameCount", A_GetFrameCount},
- {"IsScalingAllowed", A_IsScalingAllowed},
- {"IsAlphaAllowed", A_IsAlphaAllowed},
- {"IsTintingAllowed", A_IsTintingAllowed},
- {"GetCurrentFrame", A_GetCurrentFrame},
- {"GetCurrentAction", A_GetCurrentAction},
- {"IsPlaying", A_IsPlaying},
- {"RegisterLoopPointCallback", A_RegisterLoopPointCallback},
- {"UnregisterLoopPointCallback", A_UnregisterLoopPointCallback},
- {"RegisterActionCallback", A_RegisterActionCallback},
- {"UnregisterActionCallback", A_UnregisterActionCallback},
- {"Remove", A_Remove},
+ {"Play", a_play},
+ {"Pause", a_pause},
+ {"Stop", a_stop},
+ {"SetFrame", a_setFrame},
+ {"SetAlpha", a_setAlpha},
+ {"SetTintColor", a_setTintColor},
+ {"SetScaleFactor", a_setScaleFactor},
+ {"SetScaleFactorX", a_setScaleFactorX},
+ {"SetScaleFactorY", a_setScaleFactorY},
+ {"GetScaleFactorX", a_getScaleFactorX},
+ {"GetScaleFactorY", a_getScaleFactorY},
+ {"GetAnimationType", a_getAnimationType},
+ {"GetFPS", a_getFPS},
+ {"GetFrameCount", a_getFrameCount},
+ {"IsScalingAllowed", a_isScalingAllowed},
+ {"IsAlphaAllowed", a_isAlphaAllowed},
+ {"IsTintingAllowed", a_isTintingAllowed},
+ {"GetCurrentFrame", a_getCurrentFrame},
+ {"GetCurrentAction", a_getCurrentAction},
+ {"IsPlaying", a_isPlaying},
+ {"RegisterLoopPointCallback", a_registerLoopPointCallback},
+ {"UnregisterLoopPointCallback", a_unregisterLoopPointCallback},
+ {"RegisterActionCallback", a_registerActionCallback},
+ {"UnregisterActionCallback", a_unregisterActionCallback},
+ {"Remove", a_remove},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static RenderObjectPtr<Text> CheckText(lua_State *L) {
+static RenderObjectPtr<Text> checkText(lua_State *L) {
// Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Text
- uint *UserDataPtr;
- if ((UserDataPtr = (uint *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
- RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
- if (ROPtr.isValid())
- return ROPtr->toText();
+ uint *userDataPtr;
+ if ((userDataPtr = (uint *)my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> roPtr(*userDataPtr);
+ if (roPtr.isValid())
+ return roPtr->toText();
else
- luaL_error(L, "The text with the handle %d does no longer exist.", *UserDataPtr);
+ luaL_error(L, "The text with the handle %d does no longer exist.", *userDataPtr);
} else {
luaL_argcheck(L, 0, 1, "'" TEXT_CLASS_NAME "' expected");
}
@@ -1384,148 +1155,118 @@ static RenderObjectPtr<Text> CheckText(lua_State *L) {
return RenderObjectPtr<Text>();
}
-// -----------------------------------------------------------------------------
-
-static int T_SetFont(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr->SetFont(luaL_checkstring(L, 2));
+static int t_setFont(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setFont(luaL_checkstring(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int T_SetText(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr->SetText(luaL_checkstring(L, 2));
+static int t_setText(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setText(luaL_checkstring(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int T_SetAlpha(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+static int t_setAlpha(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int T_SetColor(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr->setColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+static int t_setColor(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setColor(GraphicEngine::luaColorToARGBColor(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int T_SetAutoWrap(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr->SetAutoWrap(lua_tobooleancpp(L, 2));
+static int t_setAutoWrap(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setAutoWrap(lua_tobooleancpp(L, 2));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int T_SetAutoWrapThreshold(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr->SetAutoWrapThreshold(static_cast<uint>(luaL_checknumber(L, 2)));
+static int t_setAutoWrapThreshold(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr->setAutoWrapThreshold(static_cast<uint>(luaL_checknumber(L, 2)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int T_GetText(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- lua_pushstring(L, TextPtr->GetText().c_str());
+static int t_getText(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushstring(L, textPtr->getText().c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int T_GetFont(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- lua_pushstring(L, TextPtr->GetFont().c_str());
+static int t_getFont(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushstring(L, textPtr->getFont().c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int T_GetAlpha(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- lua_pushnumber(L, TextPtr->getAlpha());
+static int t_getAlpha(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushnumber(L, textPtr->getAlpha());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int T_GetColor(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- lua_pushnumber(L, TextPtr->getColor());
+static int t_getColor(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushnumber(L, textPtr->getColor());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int T_IsAutoWrap(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- lua_pushbooleancpp(L, TextPtr->IsAutoWrapActive());
+static int t_isAutoWrap(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushbooleancpp(L, textPtr->isAutoWrapActive());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int T_GetAutoWrapThreshold(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- lua_pushnumber(L, TextPtr->GetAutoWrapThreshold());
+static int t_getAutoWrapThreshold(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ lua_pushnumber(L, textPtr->getAutoWrapThreshold());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int T_Remove(lua_State *L) {
- RenderObjectPtr<Text> TextPtr = CheckText(L);
- BS_ASSERT(TextPtr.isValid());
- TextPtr.erase();
+static int t_remove(lua_State *L) {
+ RenderObjectPtr<Text> textPtr = checkText(L);
+ BS_ASSERT(textPtr.isValid());
+ textPtr.erase();
return 0;
}
-// -----------------------------------------------------------------------------
-
static const luaL_reg TEXT_METHODS[] = {
- {"SetFont", T_SetFont},
- {"SetText", T_SetText},
- {"SetAlpha", T_SetAlpha},
- {"SetColor", T_SetColor},
- {"SetAutoWrap", T_SetAutoWrap},
- {"SetAutoWrapThreshold", T_SetAutoWrapThreshold},
- {"GetText", T_GetText},
- {"GetFont", T_GetFont},
- {"GetAlpha", T_GetAlpha},
- {"GetColor", T_GetColor},
- {"IsAutoWrap", T_IsAutoWrap},
- {"GetAutoWrapThreshold", T_GetAutoWrapThreshold},
- {"Remove", T_Remove},
+ {"SetFont", t_setFont},
+ {"SetText", t_setText},
+ {"SetAlpha", t_setAlpha},
+ {"SetColor", t_setColor},
+ {"SetAutoWrap", t_setAutoWrap},
+ {"SetAutoWrapThreshold", t_setAutoWrapThreshold},
+ {"GetText", t_getText},
+ {"GetFont", t_getFont},
+ {"GetAlpha", t_getAlpha},
+ {"GetColor", t_getColor},
+ {"IsAutoWrap", t_isAutoWrap},
+ {"GetAutoWrapThreshold", t_getAutoWrapThreshold},
+ {"Remove", t_remove},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-bool GraphicEngine::RegisterScriptBindings() {
- Kernel *pKernel = Kernel::GetInstance();
+bool GraphicEngine::registerScriptBindings() {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pScript = pKernel->getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
@@ -1544,10 +1285,21 @@ bool GraphicEngine::RegisterScriptBindings() {
if (!LuaBindhelper::addFunctionsToLib(L, GFX_LIBRARY_NAME, GFX_FUNCTIONS)) return false;
- LoopPointCallbackPtr.reset(new LuaCallback(L));
- ActionCallbackPtr.reset(new ActionCallback(L));
+ assert(loopPointCallbackPtr == 0);
+ loopPointCallbackPtr = new LuaCallback(L);
+
+ assert(actionCallbackPtr == 0);
+ actionCallbackPtr = new ActionCallback(L);
return true;
}
+void GraphicEngine::unregisterScriptBindings() {
+ delete loopPointCallbackPtr;
+ loopPointCallbackPtr = 0;
+
+ delete actionCallbackPtr;
+ actionCallbackPtr = 0;
+}
+
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/art.cpp b/engines/sword25/gfx/image/art.cpp
index e9aacbcf24..d30460901d 100644
--- a/engines/sword25/gfx/image/art.cpp
+++ b/engines/sword25/gfx/image/art.cpp
@@ -34,40 +34,13 @@
/* Various utility functions RLL finds useful. */
+#include "common/textconsole.h"
+
#include "sword25/gfx/image/art.h"
namespace Sword25 {
/**
- * art_die: Print the error message to stderr and exit with a return code of 1.
- * @fmt: The printf-style format for the error message.
- *
- * Used for dealing with severe errors.
- **/
-void art_die(const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- exit(1);
-}
-
-/**
- * art_warn: Print the warning message to stderr.
- * @fmt: The printf-style format for the warning message.
- *
- * Used for generating warnings.
- **/
-void art_warn(const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
-
-/**
* art_svp_free: Free an #ArtSVP structure.
* @svp: #ArtSVP to free.
*
@@ -551,11 +524,11 @@ static void art_svp_vpath_stroke_arc(ArtVpath **p_vpath, int *pn, int *pn_max,
if (radius > 0) {
/* curve to the left */
if (th_0 < th_1) th_0 += M_PI * 2;
- n_pts = ceil((th_0 - th_1) / theta);
+ n_pts = (int)ceil((th_0 - th_1) / theta);
} else {
/* curve to the right */
if (th_1 < th_0) th_1 += M_PI * 2;
- n_pts = ceil((th_1 - th_0) / theta);
+ n_pts = (int)ceil((th_1 - th_0) / theta);
}
art_vpath_add_point(p_vpath, pn, pn_max,
ART_LINETO, xc + x0, yc + y0);
@@ -769,7 +742,7 @@ static void render_cap(ArtVpath **p_result, int *pn_result, int *pn_result_max,
ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
break;
case ART_PATH_STROKE_CAP_ROUND:
- n_pts = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / line_width)));
+ n_pts = (int)ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / line_width)));
art_vpath_add_point(p_result, pn_result, pn_result_max,
ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
for (i = 1; i < n_pts; i++) {
@@ -1149,7 +1122,7 @@ static int art_svp_writer_rewind_add_segment(ArtSvpWriter *self, int wind_left,
ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
ArtSVP *svp;
ArtSVPSeg *seg;
- art_boolean left_filled, right_filled;
+ art_boolean left_filled = 0, right_filled = 0;
int wind_right = wind_left + delta_wind;
int seg_num;
const int init_n_points_max = 4;
@@ -1172,7 +1145,7 @@ static int art_svp_writer_rewind_add_segment(ArtSvpWriter *self, int wind_left,
right_filled = (wind_right > 0);
break;
default:
- art_die("Unknown wind rule %d\n", swr->rule);
+ error("Unknown wind rule %d", swr->rule);
}
if (left_filled == right_filled) {
/* discard segment now */
@@ -1386,7 +1359,7 @@ static void art_svp_intersect_add_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg)
ArtActiveSeg *place_right = NULL;
if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ) {
- art_warn("*** attempt to put segment in horiz list twice\n");
+ warning("attempt to put segment in horiz list twice");
return;
}
seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
@@ -1563,7 +1536,7 @@ static ArtActiveSeg *art_svp_intersect_add_point(ArtIntersectCtx *ctx, double x,
break;
new_x = x_test;
if (new_x < x_test) {
- art_warn("art_svp_intersect_add_point: non-ascending x\n");
+ warning("art_svp_intersect_add_point: non-ascending x");
}
x_test = new_x;
}
@@ -2281,7 +2254,8 @@ static void art_svp_render_insert_active(int i, int *active_segs, int n_active_s
/* this is a cheap hack to get ^'s sorted correctly */
x = seg_x[i] + 0.001 * seg_dx[i];
- for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++);
+ for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++)
+ ;
tmp1 = i;
while (j < n_active_segs) {
@@ -2438,7 +2412,8 @@ void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
svp->segs[i].bbox.x0 < x1) {
seg = &svp->segs[i];
/* move cursor to topmost vector which overlaps [y,y+1) */
- for (curs = 0; seg->points[curs + 1].y < y; curs++);
+ for (curs = 0; seg->points[curs + 1].y < y; curs++)
+ ;
cursor[i] = curs;
dy = seg->points[curs + 1].y - seg->points[curs].y;
if (fabs(dy) >= EPSILON_6)
@@ -2491,12 +2466,12 @@ void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
start += (int)delta;
else if (ix_min == ix_max) {
/* case 1, antialias a single pixel */
- xdelta = (ix_min + 1 - (x_min + x_max) * 0.5) * delta;
+ xdelta = (int)((ix_min + 1 - (x_min + x_max) * 0.5) * delta);
ADD_STEP(ix_min, xdelta)
if (ix_min + 1 < x1) {
- xdelta = delta - xdelta;
+ xdelta = (int)(delta - xdelta);
ADD_STEP(ix_min + 1, xdelta)
}
@@ -2505,8 +2480,8 @@ void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
rslope = 1.0 / fabs(seg_dx[seg_index]);
drslope = delta * rslope;
last =
- drslope * 0.5 *
- (ix_min + 1 - x_min) * (ix_min + 1 - x_min);
+ (int)(drslope * 0.5 *
+ (ix_min + 1 - x_min) * (ix_min + 1 - x_min));
xdelta = last;
if (ix_min >= x0) {
ADD_STEP(ix_min, xdelta)
@@ -2519,25 +2494,25 @@ void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
if (ix_max > x1)
ix_max = x1;
for (; x < ix_max; x++) {
- this_ = (seg->dir ? 16711680.0 : -16711680.0) * rslope *
- (x + 0.5 - x_min);
+ this_ = (int)((seg->dir ? 16711680.0 : -16711680.0) * rslope *
+ (x + 0.5 - x_min));
xdelta = this_ - last;
last = this_;
ADD_STEP(x, xdelta)
}
if (x < x1) {
- this_ =
- delta * (1 - 0.5 *
+ this_ =
+ (int)(delta * (1 - 0.5 *
(x_max - ix_max) * (x_max - ix_max) *
- rslope);
+ rslope));
xdelta = this_ - last;
last = this_;
ADD_STEP(x, xdelta)
if (x + 1 < x1) {
- xdelta = delta - last;
+ xdelta = (int)(delta - last);
ADD_STEP(x + 1, xdelta)
}
diff --git a/engines/sword25/gfx/image/b25sloader.cpp b/engines/sword25/gfx/image/b25sloader.cpp
deleted file mode 100644
index 513e74ccea..0000000000
--- a/engines/sword25/gfx/image/b25sloader.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
-#include "sword25/gfx/image/b25sloader.h"
-#include "sword25/gfx/image/pngloader.h"
-
-namespace Sword25 {
-
-#define BS_LOG_PREFIX "B25SLOADER"
-
-// -----------------------------------------------------------------------------
-
-namespace {
-static Common::String LoadString(Common::ReadStream &In, uint MaxSize = 999) {
- Common::String Result;
-
- char ch = (char)In.readByte();
- while ((ch != '\0') && (ch != ' ')) {
- Result += ch;
- if (Result.size() >= MaxSize) break;
- ch = (char)In.readByte();
- }
-
- return Result;
-}
-
-uint FindEmbeddedPNG(const byte *FileDataPtr, uint FileSize) {
- if (memcmp(FileDataPtr, "BS25SAVEGAME", 12))
- return 0;
-
- // Read in the header
- Common::MemoryReadStream stream(FileDataPtr, FileSize);
-
- // Headerinformationen der Spielstandes einlesen.
- uint compressedGamedataSize;
- LoadString(stream);
- LoadString(stream);
- Common::String gameSize = LoadString(stream);
- compressedGamedataSize = atoi(gameSize.c_str());
- LoadString(stream);
-
- // Return the offset of where the thumbnail starts
- return static_cast<uint>(stream.pos() + compressedGamedataSize);
-}
-}
-
-// -----------------------------------------------------------------------------
-
-bool B25SLoader::IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {
- // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
- uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
- if (PNGOffset > 0) {
- return PNGLoader::DoIsCorrectImageFormat(FileDataPtr + PNGOffset, FileSize - PNGOffset);
- }
-
- return false;
-}
-
-// -----------------------------------------------------------------------------
-
-bool B25SLoader::DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
- int &Width, int &Height, int &Pitch) {
- // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
- uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
- if (PNGOffset > 0) {
- return PNGLoader::DoDecodeImage(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);
- }
-
- return false;
-}
-
-// -----------------------------------------------------------------------------
-
-bool B25SLoader::ImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height) {
- // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
- uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
- if (PNGOffset > 0) {
- return PNGLoader::DoImageProperties(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, Width, Height);
- }
-
- return false;
-}
-
-} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/b25sloader.h b/engines/sword25/gfx/image/b25sloader.h
deleted file mode 100644
index fbfaf87194..0000000000
--- a/engines/sword25/gfx/image/b25sloader.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-#ifndef SWORD25_B25SLOADER_H
-#define SWORD25_B25SLOADER_H
-
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
-#include "sword25/kernel/common.h"
-#include "sword25/gfx/image/imageloader.h"
-
-namespace Sword25 {
-
-// -----------------------------------------------------------------------------
-// Klassendeklaration
-// -----------------------------------------------------------------------------
-
-class B25SLoader : public ImageLoader {
-public:
- static ImageLoader *CreateInstance() {
- return static_cast<ImageLoader *>(new B25SLoader());
- }
-
-protected:
- virtual bool IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize);
- virtual bool DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
- int &Width, int &Height, int &Pitch);
- virtual bool ImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height);
-
-};
-
-} // End of namespace Sword25
-
-#endif
diff --git a/engines/sword25/gfx/image/image.h b/engines/sword25/gfx/image/image.h
index bc0ff20d77..5ac6d1ac25 100644
--- a/engines/sword25/gfx/image/image.h
+++ b/engines/sword25/gfx/image/image.h
@@ -51,7 +51,7 @@ namespace Sword25 {
class Image {
public:
- virtual ~Image() {};
+ virtual ~Image() {}
// Enums
/**
diff --git a/engines/sword25/gfx/image/imageloader.cpp b/engines/sword25/gfx/image/imageloader.cpp
deleted file mode 100644
index 55ce833cc9..0000000000
--- a/engines/sword25/gfx/image/imageloader.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-#include "sword25/gfx/image/imageloader.h"
-#include "sword25/gfx/image/imageloader_ids.h"
-
-namespace Sword25 {
-
-#define BS_LOG_PREFIX "IMAGELOADER"
-
-// Statische Elemente der Klasse BS_ImageLoader intialisieren.
-Common::List<ImageLoader *> ImageLoader::_ImageLoaderList;
-bool ImageLoader::_ImageLoaderListInitialized = false;
-
-// Lade Methode
-// ------------
-
-bool ImageLoader::LoadImage(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS ColorFormat,
- byte *&pUncompressedData,
- int &Width, int &Height,
- int &Pitch) {
- // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.
- if (!_ImageLoaderListInitialized)
- _InitializeLoaderList();
-
- // Passenden BS_ImageLoader finden und Bild dekodieren
- ImageLoader *pLoader = _FindSuitableImageLoader(pFileData, FileSize);
- if (pLoader) {
- return pLoader->DecodeImage(pFileData, FileSize,
- ColorFormat,
- pUncompressedData,
- Width, Height,
- Pitch);
- }
-
- return false;
-}
-
-// Info Methode
-// ------------
-
-bool ImageLoader::ExtractImageProperties(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS &ColorFormat,
- int &Width, int &Height) {
- // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.
- if (!_ImageLoaderListInitialized)
- _InitializeLoaderList();
-
- // Passenden BS_ImageLoader finden und Bildeigenschaften auslesen.
- ImageLoader *pLoader = _FindSuitableImageLoader(pFileData, FileSize);
- if (pLoader) {
- return pLoader->ImageProperties(pFileData, FileSize,
- ColorFormat,
- Width, Height);
- }
-
- return false;
-}
-
-// Verwaltungs Methoden
-// --------------------
-
-void ImageLoader::_InitializeLoaderList() {
- // Von jedem BS_ImageLoader wird eine Instanz erzeugt, diese fügen sich selbständig in die BS_ImageLoader-Liste ein.
- for (int i = 0; i < BS_IMAGELOADER_COUNT; i++)
- BS_IMAGELOADER_IDS[i]();
-
- // Die Liste als gefüllt markieren.
- _ImageLoaderListInitialized = true;
-
- // Sicherstellen, dass beim Beenden alle BS_ImageLoader Instanzen zerstört werden.
- atexit(ImageLoader::_DeinitializeLoaderList);
-}
-
-void ImageLoader::_DeinitializeLoaderList() {
- while (!_ImageLoaderList.empty()) {
- delete _ImageLoaderList.back();
- _ImageLoaderList.pop_back();
- }
-}
-
-ImageLoader *ImageLoader::_FindSuitableImageLoader(const byte *pFileData, uint FileSize) {
- // Alle BS_ImageLoader-Objekte durchgehen, bis eins gefunden wurde, dass das Bild laden kann
- Common::List<ImageLoader *>::iterator Iter = _ImageLoaderList.begin();
- for (; Iter != _ImageLoaderList.end(); ++Iter) {
- // Falls ein geeigneter BS-ImageLoader gefunden wurde, wird er zurückgegeben.
- if ((*Iter)->IsCorrectImageFormat(pFileData, FileSize)) {
- return (*Iter);
- }
- }
-
- // Es konnte kein passender BS_ImageLoader gefunden werden.
- BS_LOG_ERRORLN("Could not find suitable image loader for image data.");
- return NULL;
-}
-
-} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/imageloader.h b/engines/sword25/gfx/image/imageloader.h
deleted file mode 100644
index aae48a083c..0000000000
--- a/engines/sword25/gfx/image/imageloader.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-/*
- BS_ImageLoader
- --------------
-
- Autor: Malte Thiesen
-*/
-
-#ifndef SWORD25_IMAGELOADER_H
-#define SWORD25_IMAGELOADER_H
-
-// Includes
-#include "sword25/kernel/bs_stdint.h"
-#include "sword25/kernel/common.h"
-#include "sword25/gfx/graphicengine.h"
-
-namespace Sword25 {
-
-/**
- @brief Über die statischen Methoden dieser Klasse werden alle unterstützten Bildformate geladen.
-
- Zum Laden von Bildern wird die #LoadImage-Methode benutzt.
-
- Außerdem stellt diese Klasse das Interface da, das alle Klassen implementieren müssen, die Bildformate einlesen.<br>
- Zur Unterstützung eines neuen Bildformates muss folgendermaßen vorgegangen werden:
- - Erzeugen einer neuen von #BS_ImageLoader abgeleiteten Klasse, die die Methoden #IsCorrectImageFormat und #DecodeImage impelementiert.
- - Die Klasse muss eine statische Methode haben, die eine Instanz von ihr erzeugt und einen Pointer darauf zurückgibt.
- - Diese Methode muss in der Liste in der Datei imageloader_ids.h eingetragen werden.
- - Die Klasse muss JEDES Eingabebild seines Bildformates in die folgenden Farbformate konvertieren können:
- - BS_GraphicEngine::CF_ARGB32
- - Zum Konvertieren der Bilddaten können die Hilfsmethoden dieser Klasse benutzt werden, die ARGB Bilddaten in alle benötigten
- Farbformate konvertieren.
-*/
-class ImageLoader {
-public:
-
- //@{
- /** @name Lade Methoden */
-
- /**
- @brief Lädt eine Bilddatei.
-
- Diese Methode kann sämtliche unterstütztem Bildformate lesen. Die Methode erkennt selbstständing um welches Dateiformat es sich
- bei der vorliegenden Datei handelt.<br>
- Bisher wird nur PNG unterstützt.
-
- @param pFileData ein Pointer auf die Bilddaten.
- @param FileSize die Größe der Bilddaten in Byte.
- @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br>
- Folgende Farbformate werden unterstützt:
- - BS_GraphicEngine::CF_ARGB32
- @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten.
- @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an.
- @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an.
- @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an.
- @return Gibt false zurück, falls das Laden fehlgeschlagen ist.
- @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height.
- @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
- */
- static bool LoadImage(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS ColorFormat,
- byte *&pUncompressedData,
- int &Width, int &Height,
- int &Pitch);
-
- /**
- @brief Liest die Bildeigenschaften eines Bildes aus.
-
- @param pFileData ein Pointer auf die Bilddaten.
- @param FileSize die Größe des Bilddaten in Byte.
- @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes.
- @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln.
- @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln.
- @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten.
- @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
- */
- static bool ExtractImageProperties(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS &ColorFormat,
- int &Width, int &Height);
- //@}
-
-protected:
-
- // Protected Konstruktor, damit Instanzen dieser Klasse nur von BS_ImageLoader-Objekten erstellt werden können
- /**
- @brief Der Standardkonstruktor.
-
- Dieser Konstruktor registriert alle Instanzen von #BS_ImageLoader-Klassen in einer Liste.<br>
- Diese Liste enthält jeweils eine Instanz jedes #BS_ImageLoader und wird benutzt um beliebige Bilddateien einem Loader zuzuordnen.
- @remark Dieser Konstruktor ist protected damit nur #BS_ImageLoader-Objekte diese Klasse instanziieren können.
- */
- ImageLoader() {
- // Klasse registrieren
- _ImageLoaderList.push_front(this);
- }
-
- virtual ~ImageLoader() {}
-
- //@{
- /** @name Abstrakte Methoden */
-
- /**
- @brief Gibt an, ob der #BS_ImageLoader ein Bild lesen kann.
- @param pFileData ein Pointer auf die kompletten Daten des Bildes.
- @param FileSize die Größe der Daten in Byte.
- @return Gibt true zurück, wenn der #BS_ImageLoader das Bild lesen kann, ansonsten false.
- @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
- */
- virtual bool IsCorrectImageFormat(const byte *pFileData, uint FileSize) = 0;
-
- /**
- @brief Lädt eine Bilddatei.
- @param pFileData ein Pointer auf die Bilddaten.
- @param FileSize die Größe der Bilddaten in Byte.
- @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br>
- Folgende Farbformate werden unterstützt:
- - BS_GraphicEngine::CF_ARGB32
- @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten.
- @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an.
- @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an.
- @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an.
- @return Gibt false zurück, falls das Laden fehlgeschlagen ist.
- @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height.
- @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
- @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
- */
- virtual bool DecodeImage(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS ColorFormat,
- byte *&pUncompressedData,
- int &Width, int &Height,
- int &Pitch) = 0;
-
- /**
- @brief Liest die Bildeigenschaften aus.
- @param pFileData ein Pointer auf die Bilddaten.
- @param FileSize die Größe des Bilddaten in Byte.
- @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes.
- @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln.
- @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln.
- @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten.
- @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
- @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
- */
- virtual bool ImageProperties(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS &ColorFormat,
- int &Width, int &Height) = 0;
-
- //@}
-
-private:
-
- /**
- @brief Erzeugt je eine Instanz aller BS_ImageLoader Klassen und fügt diese in eine interne Liste ein. Diese werden dann beim
- Laden von Bildern benutzt.
- @remark Die Klassen müssen in der Datei imageloader_ids.h eingetragen sein, damit sie an dieser Stelle berücksichtigt werden.
- */
- static void _InitializeLoaderList();
-
- /**
- @brief Zerstört alle Instanzen von BS_ImageLoader Klassen, die in dieser Klasse registriert sind.
- */
- static void _DeinitializeLoaderList();
-
- /**
- @brief Sucht zu Bilddaten ein BS_ImageLoader Objekt, dass die Bilddaten dekodieren kann.
- @return Gibt einen Pointer auf ein passendes BS_ImageLoader Objekt zurück, oder NULL, wenn kein passendes Objekt gefunden wurde.
- */
- static ImageLoader *_FindSuitableImageLoader(const byte *pFileData, uint FileSize);
-
- static Common::List<ImageLoader *> _ImageLoaderList; // Die Liste aller BS_ImageLoader-Objekte
- static bool _ImageLoaderListInitialized; // Gibt an, ob die Liste schon intialisiert wurde
-};
-
-} // End of namespace Sword25
-
-#endif
diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp
index c59d68724d..581d5fd917 100644
--- a/engines/sword25/gfx/image/pngloader.cpp
+++ b/engines/sword25/gfx/image/pngloader.cpp
@@ -32,10 +32,10 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
+// Disable symbol overrides so that we can use png.h
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/memstream.h"
#include "sword25/gfx/image/image.h"
#include "sword25/gfx/image/pngloader.h"
#include <png.h>
@@ -44,44 +44,74 @@ namespace Sword25 {
#define BS_LOG_PREFIX "PNGLOADER"
-// -----------------------------------------------------------------------------
-// Konstruktor / Destruktor
-// -----------------------------------------------------------------------------
-PNGLoader::PNGLoader() {
+/**
+ * Load a NULL-terminated string from the given stream.
+ */
+static Common::String loadString(Common::ReadStream &in, uint maxSize = 999) {
+ Common::String result;
+
+ while (!in.eos() && (result.size() < maxSize)) {
+ char ch = (char)in.readByte();
+ if (ch == '\0')
+ break;
+
+ result += ch;
+ }
+
+ return result;
}
-// -----------------------------------------------------------------------------
-// Laden
-// -----------------------------------------------------------------------------
+/**
+ * Check if the given data is a savegame, and if so, locate the
+ * offset to the image data.
+ * @return offset to image data if fileDataPtr contains a savegame; 0 otherwise
+ */
+static uint findEmbeddedPNG(const byte *fileDataPtr, uint fileSize) {
+ if (fileSize < 100)
+ return 0;
+ if (memcmp(fileDataPtr, "BS25SAVEGAME", 12))
+ return 0;
+
+ // Read in the header
+ Common::MemoryReadStream stream(fileDataPtr, fileSize);
+ stream.seek(0, SEEK_SET);
+
+ // Headerinformationen der Spielstandes einlesen.
+ uint compressedGamedataSize;
+ loadString(stream); // Marker
+ loadString(stream); // Version
+ loadString(stream); // Description
+ Common::String gameSize = loadString(stream);
+ compressedGamedataSize = atoi(gameSize.c_str());
+ loadString(stream);
+
+ // Return the offset of where the thumbnail starts
+ return static_cast<uint>(stream.pos() + compressedGamedataSize);
+}
static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
- memcpy(data, (char *)png_ptr->io_ptr, length);
- png_ptr->io_ptr = (void *)((png_size_t)png_ptr->io_ptr + length);
+ const byte **ref = (const byte **)png_get_io_ptr(png_ptr);
+ memcpy(data, *ref, length);
+ *ref += length;
+}
+
+static bool doIsCorrectImageFormat(const byte *fileDataPtr, uint fileSize) {
+ return (fileSize > 8) && png_check_sig(const_cast<byte *>(fileDataPtr), 8);
}
-// -----------------------------------------------------------------------------
-bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
- int &Width, int &Height, int &Pitch) {
+bool PNGLoader::doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
- png_bytep RawDataBuffer = NULL;
- png_bytep *pRowPtr = NULL;
- int BitDepth;
- int ColorType;
- int InterlaceType;
+ int bitDepth;
+ int colorType;
+ int interlaceType;
int i;
- // Zielfarbformat überprüfen
- if (ColorFormat != GraphicEngine::CF_ARGB32) {
- BS_LOG_ERRORLN("Illegal or unsupported color format.");
- return false;
- }
-
- // PNG Signatur überprüfen
- if (!png_check_sig(reinterpret_cast<png_bytep>(const_cast<byte *>(FileDataPtr)), 8)) {
+ // Check for valid PNG signature
+ if (!doIsCorrectImageFormat(fileDataPtr, fileSize)) {
error("png_check_sig failed");
}
@@ -97,127 +127,92 @@ bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEn
}
// Alternative Lesefunktion benutzen
- png_set_read_fn(png_ptr, (void *)FileDataPtr, png_user_read_data);
+ const byte **ref = &fileDataPtr;
+ png_set_read_fn(png_ptr, (void *)ref, png_user_read_data);
// PNG Header einlesen
png_read_info(png_ptr, info_ptr);
// PNG Informationen auslesen
- png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&Width, (png_uint_32 *)&Height, &BitDepth, &ColorType, &InterlaceType, NULL, NULL);
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitDepth, &colorType, &interlaceType, NULL, NULL);
// Pitch des Ausgabebildes berechnen
- Pitch = GraphicEngine::CalcPitch(ColorFormat, Width);
+ pitch = GraphicEngine::calcPitch(GraphicEngine::CF_ARGB32, width);
// Speicher für die endgültigen Bilddaten reservieren
// Dieses geschieht vor dem reservieren von Speicher für temporäre Bilddaten um die Fragmentierung des Speichers gering zu halten
- UncompressedDataPtr = new byte[Pitch * Height];
- if (!UncompressedDataPtr) {
+ uncompressedDataPtr = new byte[pitch * height];
+ if (!uncompressedDataPtr) {
error("Could not allocate memory for output image.");
}
// Bilder jeglicher Farbformate werden zunächst in ARGB Bilder umgewandelt
- if (BitDepth == 16)
+ if (bitDepth == 16)
png_set_strip_16(png_ptr);
- if (ColorType == PNG_COLOR_TYPE_PALETTE)
+ if (colorType == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
- if (BitDepth < 8)
+ if (bitDepth < 8)
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand(png_ptr);
- if (ColorType == PNG_COLOR_TYPE_GRAY ||
- ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ if (colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
png_set_bgr(png_ptr);
- if (ColorType != PNG_COLOR_TYPE_RGB_ALPHA)
+ if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
// Nachdem die Transformationen registriert wurden, werden die Bilddaten erneut eingelesen
png_read_update_info(png_ptr, info_ptr);
- png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&Width, (png_uint_32 *)&Height, &BitDepth, &ColorType, NULL, NULL, NULL);
-
- // PNGs ohne Interlacing werden Zeilenweise eingelesen
- if (InterlaceType == PNG_INTERLACE_NONE) {
- // Speicher für eine Bildzeile reservieren
- RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
- if (!RawDataBuffer) {
- error("Could not allocate memory for row buffer.");
- }
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitDepth, &colorType, NULL, NULL, NULL);
- // Bilddaten zeilenweise einlesen und in das gewünschte Zielformat konvertieren
- for (i = 0; i < Height; i++) {
- // Zeile einlesen
- png_read_row(png_ptr, RawDataBuffer, NULL);
-
- // Zeile konvertieren
- switch (ColorFormat) {
- case GraphicEngine::CF_ARGB32:
- memcpy(&UncompressedDataPtr[i * Pitch], RawDataBuffer, Pitch);
- break;
- default:
- assert(0);
- }
+ if (interlaceType == PNG_INTERLACE_NONE) {
+ // PNGs without interlacing can simply be read row by row.
+ for (i = 0; i < height; i++) {
+ png_read_row(png_ptr, uncompressedDataPtr + i * pitch, NULL);
}
} else {
- // PNGs mit Interlacing werden an einem Stück eingelesen
- // Speicher für das komplette Bild reservieren
- RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr) * Height];
- if (!RawDataBuffer) {
- error("Could not allocate memory for raw image buffer.");
- }
+ // PNGs with interlacing require us to allocate an auxillary
+ // buffer with pointers to all row starts.
- // Speicher für die Rowpointer reservieren
- pRowPtr = new png_bytep[Height];
+ // Allocate row pointer buffer
+ png_bytep *pRowPtr = new png_bytep[height];
if (!pRowPtr) {
error("Could not allocate memory for row pointers.");
}
- // Alle Rowpointer mit den richtigen Offsets initialisieren
- for (i = 0; i < Height; i++)
- pRowPtr[i] = RawDataBuffer + i * png_get_rowbytes(png_ptr, info_ptr);
+ // Initialize row pointers
+ for (i = 0; i < height; i++)
+ pRowPtr[i] = uncompressedDataPtr + i * pitch;
- // Bild einlesen
+ // Read image data
png_read_image(png_ptr, pRowPtr);
- // Bilddaten zeilenweise in das gewünschte Ausgabeformat konvertieren
- switch (ColorFormat) {
- case GraphicEngine::CF_ARGB32:
- for (i = 0; i < Height; i++)
- memcpy(&UncompressedDataPtr[i * Pitch], &RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)], Pitch);
- break;
- default:
- error("Unhandled case in DoDecodeImage");
- break;
- }
+ // Free row pointer buffer
+ delete[] pRowPtr;
}
- // Die zusätzlichen Daten am Ende des Bildes lesen
+ // Read additional data at the end.
png_read_end(png_ptr, NULL);
- // Die Strukturen freigeben
+ // Destroy libpng structures
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- // Temporäre Buffer freigeben
- delete[] pRowPtr;
- delete[] RawDataBuffer;
-
- // Der Funktionsaufruf war erfolgreich
+ // Signal success
return true;
}
-// -----------------------------------------------------------------------------
-
-bool PNGLoader::DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
- int &Width, int &Height, int &Pitch) {
- return DoDecodeImage(FileDataPtr, FileSize, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);
+bool PNGLoader::decodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
+ uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize);
+ return doDecodeImage(fileDataPtr + pngOffset, fileSize - pngOffset, uncompressedDataPtr, width, height, pitch);
}
-// -----------------------------------------------------------------------------
-
-bool PNGLoader::DoImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height) {
- // PNG Signatur überprüfen
- if (!DoIsCorrectImageFormat(FileDataPtr, FileSize)) return false;
+bool PNGLoader::doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) {
+ // Check for valid PNG signature
+ if (!doIsCorrectImageFormat(fileDataPtr, fileSize))
+ return false;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
@@ -234,21 +229,16 @@ bool PNGLoader::DoImageProperties(const byte *FileDataPtr, uint FileSize, Graphi
}
// Alternative Lesefunktion benutzen
- png_set_read_fn(png_ptr, (void *)FileDataPtr, png_user_read_data);
+ const byte **ref = &fileDataPtr;
+ png_set_read_fn(png_ptr, (void *)ref, png_user_read_data);
// PNG Header einlesen
png_read_info(png_ptr, info_ptr);
// PNG Informationen auslesen
- int BitDepth;
- int ColorType;
- png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&Width, (png_uint_32 *)&Height, &BitDepth, &ColorType, NULL, NULL, NULL);
-
- // PNG-ColorType in BS ColorFormat konvertieren.
- if (ColorType & PNG_COLOR_MASK_ALPHA || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
- ColorFormat = GraphicEngine::CF_ARGB32;
- else
- ColorFormat = GraphicEngine::CF_RGB24;
+ int bitDepth;
+ int colorType;
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitDepth, &colorType, NULL, NULL, NULL);
// Die Strukturen freigeben
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
@@ -257,27 +247,10 @@ bool PNGLoader::DoImageProperties(const byte *FileDataPtr, uint FileSize, Graphi
}
-// -----------------------------------------------------------------------------
-
-bool PNGLoader::ImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height) {
- return DoImageProperties(FileDataPtr, FileSize, ColorFormat, Width, Height);
+bool PNGLoader::imageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) {
+ uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize);
+ return doImageProperties(fileDataPtr + pngOffset, fileSize - pngOffset, width, height);
}
-// -----------------------------------------------------------------------------
-// Header überprüfen
-// -----------------------------------------------------------------------------
-
-bool PNGLoader::DoIsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {
- if (FileSize > 8)
- return png_check_sig(const_cast<byte *>(FileDataPtr), 8) ? true : false;
- else
- return false;
-}
-
-// -----------------------------------------------------------------------------
-
-bool PNGLoader::IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {
- return DoIsCorrectImageFormat(FileDataPtr, FileSize);
-}
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/pngloader.h b/engines/sword25/gfx/image/pngloader.h
index 44c31b267c..e0d68ff8b9 100644
--- a/engines/sword25/gfx/image/pngloader.h
+++ b/engines/sword25/gfx/image/pngloader.h
@@ -32,46 +32,60 @@
*
*/
-/*
- BS_PNGLoader
- ------------
- BS_ImageLoader-Klasse zum Laden von PNG-Dateien
-
- Autor: Malte Thiesen
-*/
-
#ifndef SWORD25_PNGLOADER2_H
#define SWORD25_PNGLOADER2_H
-// Includes
#include "sword25/kernel/common.h"
-#include "sword25/gfx/image/imageloader.h"
+#include "sword25/gfx/graphicengine.h"
namespace Sword25 {
-// Klassendefinition
-class PNGLoader : public ImageLoader {
-public:
- static ImageLoader *CreateInstance() {
- return (ImageLoader *) new PNGLoader();
- }
+/**
+ * Class for loading PNG files, and PNG data embedded into savegames.
+ *
+ * Originally written by Malte Thiesen.
+ */
+class PNGLoader {
+protected:
+ PNGLoader() {} // Protected constructor to prevent instances
- // Alle virtuellen Methoden von BS_ImageLoader sind hier als static-Methode implementiert, damit sie von BS_B25SLoader aufgerufen werden können.
- // Die virtuellen Methoden rufen diese Methoden auf.
- static bool DoIsCorrectImageFormat(const byte *FileDataPtr, uint FileSize);
- static bool DoDecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
- int &Width, int &Height, int &Pitch);
- static bool DoImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height);
+ static bool doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch);
+ static bool doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height);
-protected:
- PNGLoader();
- bool DecodeImage(const byte *pFileData, uint FileSize,
- GraphicEngine::COLOR_FORMATS ColorFormat,
- byte *&pUncompressedData,
- int &Width, int &Height,
- int &Pitch);
- bool IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize);
- bool ImageProperties(const byte *FileDatePtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height);
+public:
+
+ /**
+ * Decode an image.
+ * @param[in] fileDatePtr pointer to the image data
+ * @param[in] fileSize size of the image data in bytes
+ * @param[out] pUncompressedData if successful, this is set to a pointer containing the decoded image data
+ * @param[out] width if successful, this is set to the width of the image
+ * @param[out] height if successful, this is set to the height of the image
+ * @param[out] pitch if successful, this is set to the number of bytes per scanline in the image
+ * @return false in case of an error
+ *
+ * @remark The size of the output data equals pitch * height.
+ * @remark This function does not free the image buffer passed to it,
+ * it is the callers responsibility to do so.
+ */
+ static bool decodeImage(const byte *pFileData, uint fileSize,
+ byte *&pUncompressedData,
+ int &width, int &height,
+ int &pitch);
+ /**
+ * Extract the properties of an image.
+ * @param[in] fileDatePtr pointer to the image data
+ * @param[in] fileSize size of the image data in bytes
+ * @param[out] width if successful, this is set to the width of the image
+ * @param[out] height if successful, this is set to the height of the image
+ * @return returns true if extraction of the properties was successful, false in case of an error
+ *
+ * @remark This function does not free the image buffer passed to it,
+ * it is the callers responsibility to do so.
+ */
+ static bool imageProperties(const byte *fileDatePtr, uint fileSize,
+ int &width,
+ int &height);
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp
index 96b6139a59..fb82f893c6 100644
--- a/engines/sword25/gfx/image/renderedimage.cpp
+++ b/engines/sword25/gfx/image/renderedimage.cpp
@@ -37,7 +37,7 @@
// -----------------------------------------------------------------------------
#include "sword25/package/packagemanager.h"
-#include "sword25/gfx/image/imageloader.h"
+#include "sword25/gfx/image/pngloader.h"
#include "sword25/gfx/image/renderedimage.h"
#include "common/system.h"
@@ -56,30 +56,30 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) :
_height(0) {
result = false;
- PackageManager *pPackage = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
BS_ASSERT(pPackage);
- _backSurface = (static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx")))->getSurface();
+ _backSurface = Kernel::getInstance()->getGfx()->getSurface();
// Datei laden
byte *pFileData;
uint fileSize;
- if (!(pFileData = (byte *)pPackage->getFile(filename, &fileSize))) {
+ pFileData = pPackage->getFile(filename, &fileSize);
+ if (!pFileData) {
BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
return;
}
// Bildeigenschaften bestimmen
- GraphicEngine::COLOR_FORMATS colorFormat;
int pitch;
- if (!ImageLoader::ExtractImageProperties(pFileData, fileSize, colorFormat, _width, _height)) {
+ if (!PNGLoader::imageProperties(pFileData, fileSize, _width, _height)) {
BS_LOG_ERRORLN("Could not read image properties.");
delete[] pFileData;
return;
}
// Das Bild dekomprimieren
- if (!ImageLoader::LoadImage(pFileData, fileSize, GraphicEngine::CF_ARGB32, _data, _width, _height, pitch)) {
+ if (!PNGLoader::decodeImage(pFileData, fileSize, _data, _width, _height, pitch)) {
BS_LOG_ERRORLN("Could not decode image.");
delete[] pFileData;
return;
@@ -103,7 +103,7 @@ RenderedImage::RenderedImage(uint width, uint height, bool &result) :
_data = new byte[width * height * 4];
Common::set_to(_data, &_data[width * height * 4], 0);
- _backSurface = (static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx")))->getSurface();
+ _backSurface = Kernel::getInstance()->getGfx()->getSurface();
_doCleanup = true;
@@ -112,7 +112,7 @@ RenderedImage::RenderedImage(uint width, uint height, bool &result) :
}
RenderedImage::RenderedImage() : _width(0), _height(0), _data(0) {
- _backSurface = (static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx")))->getSurface();
+ _backSurface = Kernel::getInstance()->getGfx()->getSurface();
_doCleanup = false;
diff --git a/engines/sword25/gfx/image/swimage.cpp b/engines/sword25/gfx/image/swimage.cpp
index ac4463ea16..84aa35b0f4 100644
--- a/engines/sword25/gfx/image/swimage.cpp
+++ b/engines/sword25/gfx/image/swimage.cpp
@@ -32,12 +32,8 @@
*
*/
-// -----------------------------------------------------------------------------
-// INCLUDES
-// -----------------------------------------------------------------------------
-
#include "sword25/package/packagemanager.h"
-#include "sword25/gfx/image/imageloader.h"
+#include "sword25/gfx/image/pngloader.h"
#include "sword25/gfx/image/swimage.h"
namespace Sword25 {
@@ -45,38 +41,34 @@ namespace Sword25 {
#define BS_LOG_PREFIX "SWIMAGE"
-// -----------------------------------------------------------------------------
-// CONSTRUCTION / DESTRUCTION
-// -----------------------------------------------------------------------------
-
SWImage::SWImage(const Common::String &filename, bool &result) :
_imageDataPtr(0),
_width(0),
_height(0) {
result = false;
- PackageManager *pPackage = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
BS_ASSERT(pPackage);
// Datei laden
byte *pFileData;
uint fileSize;
- if (!(pFileData = (byte *)pPackage->getFile(filename, &fileSize))) {
+ pFileData = pPackage->getFile(filename, &fileSize);
+ if (!pFileData) {
BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
return;
}
// Bildeigenschaften bestimmen
- GraphicEngine::COLOR_FORMATS colorFormat;
int pitch;
- if (!ImageLoader::ExtractImageProperties(pFileData, fileSize, colorFormat, _width, _height)) {
+ if (!PNGLoader::imageProperties(pFileData, fileSize, _width, _height)) {
BS_LOG_ERRORLN("Could not read image properties.");
return;
}
// Das Bild dekomprimieren
byte *pUncompressedData;
- if (!ImageLoader::LoadImage(pFileData, fileSize, GraphicEngine::CF_ARGB32, pUncompressedData, _width, _height, pitch)) {
+ if (!PNGLoader::decodeImage(pFileData, fileSize, pUncompressedData, _width, _height, pitch)) {
BS_LOG_ERRORLN("Could not decode image.");
return;
}
@@ -90,15 +82,11 @@ SWImage::SWImage(const Common::String &filename, bool &result) :
return;
}
-// -----------------------------------------------------------------------------
-
SWImage::~SWImage() {
delete[] _imageDataPtr;
}
-// -----------------------------------------------------------------------------
-
bool SWImage::blit(int posX, int posY,
int flipping,
Common::Rect *pPartRect,
@@ -108,22 +96,16 @@ bool SWImage::blit(int posX, int posY,
return false;
}
-// -----------------------------------------------------------------------------
-
bool SWImage::fill(const Common::Rect *pFillRect, uint color) {
BS_LOG_ERRORLN("Fill() is not supported.");
return false;
}
-// -----------------------------------------------------------------------------
-
bool SWImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
BS_LOG_ERRORLN("SetContent() is not supported.");
return false;
}
-// -----------------------------------------------------------------------------
-
uint SWImage::getPixel(int x, int y) {
BS_ASSERT(x >= 0 && x < _width);
BS_ASSERT(y >= 0 && y < _height);
diff --git a/engines/sword25/gfx/image/swimage.h b/engines/sword25/gfx/image/swimage.h
index caf6bdcc71..a914c4f41f 100644
--- a/engines/sword25/gfx/image/swimage.h
+++ b/engines/sword25/gfx/image/swimage.h
@@ -35,10 +35,6 @@
#ifndef SWORD25_SWIMAGE_H
#define SWORD25_SWIMAGE_H
-// -----------------------------------------------------------------------------
-// INCLUDES
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/gfx/image/image.h"
#include "sword25/gfx/graphicengine.h"
@@ -46,10 +42,6 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// CLASS DEFINITION
-// -----------------------------------------------------------------------------
-
class SWImage : public Image {
public:
SWImage(const Common::String &filename, bool &result);
diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp
index c2a80cb5f2..5c15c4771a 100644
--- a/engines/sword25/gfx/image/vectorimage.cpp
+++ b/engines/sword25/gfx/image/vectorimage.cpp
@@ -36,7 +36,6 @@
// Includes
// -----------------------------------------------------------------------------
-#include "sword25/kernel/bs_stdint.h"
#include "sword25/gfx/image/art.h"
#include "sword25/gfx/image/vectorimage.h"
#include "sword25/gfx/image/renderedimage.h"
diff --git a/engines/sword25/gfx/image/vectorimagerenderer.cpp b/engines/sword25/gfx/image/vectorimagerenderer.cpp
index 16d1abf9f9..7587aab4e5 100644
--- a/engines/sword25/gfx/image/vectorimagerenderer.cpp
+++ b/engines/sword25/gfx/image/vectorimagerenderer.cpp
@@ -248,9 +248,9 @@ void art_rgb_svp_alpha1(const ArtSVP *svp,
}
static int art_vpath_len(ArtVpath *a) {
- int i;
-
- for (i = 0; a[i].code != ART_END; i++);
+ int i = 0;
+ while (a[i].code != ART_END)
+ i++;
return i;
}
@@ -329,17 +329,17 @@ void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int heig
#if 0
const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
for (int i = 0;; i++) {
- printf(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
+ debugN(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
if (bez[i].code == ART_END)
break;
if (bez[i].code == ART_CURVETO) {
- printf(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
- printf(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
+ debugN(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
+ debugN(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
}
- printf(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
+ debugN(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
}
- printf(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
+ debugN(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
#endif
// HACK: Some frames have green bounding boxes drawn.
@@ -382,7 +382,7 @@ void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int heig
art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
free(vect);
- free(svp);
+ art_svp_free(svp);
free(vec);
}
diff --git a/engines/sword25/gfx/panel.cpp b/engines/sword25/gfx/panel.cpp
index 3aa0516835..2de1de133a 100644
--- a/engines/sword25/gfx/panel.cpp
+++ b/engines/sword25/gfx/panel.cpp
@@ -32,10 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/gfx/panel.h"
#include "sword25/kernel/inputpersistenceblock.h"
@@ -45,14 +41,8 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-
#define BS_LOG_PREFIX "PANEL"
-// -----------------------------------------------------------------------------
-// Construction/Destruction
-// -----------------------------------------------------------------------------
-
Panel::Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color) :
RenderObject(parentPtr, RenderObject::TYPE_PANEL),
_color(color) {
@@ -74,37 +64,25 @@ Panel::Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uin
_initSuccess = true;
}
-// -----------------------------------------------------------------------------
-
Panel::Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
RenderObject(parentPtr, RenderObject::TYPE_PANEL, handle) {
_initSuccess = unpersist(reader);
}
-// -----------------------------------------------------------------------------
-
Panel::~Panel() {
}
-// -----------------------------------------------------------------------------
-// Rendern
-// -----------------------------------------------------------------------------
-
bool Panel::doRender() {
// Falls der Alphawert 0 ist, ist das Panel komplett durchsichtig und es muss nichts gezeichnet werden.
if (_color >> 24 == 0)
return true;
- GraphicEngine *gfxPtr = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
BS_ASSERT(gfxPtr);
return gfxPtr->fill(&_bbox, _color);
}
-// -----------------------------------------------------------------------------
-// Persistenz
-// -----------------------------------------------------------------------------
-
bool Panel::persist(OutputPersistenceBlock &writer) {
bool result = true;
@@ -116,8 +94,6 @@ bool Panel::persist(OutputPersistenceBlock &writer) {
return result;
}
-// -----------------------------------------------------------------------------
-
bool Panel::unpersist(InputPersistenceBlock &reader) {
bool result = true;
diff --git a/engines/sword25/gfx/panel.h b/engines/sword25/gfx/panel.h
index 5fbcec5f34..6fe96369a6 100644
--- a/engines/sword25/gfx/panel.h
+++ b/engines/sword25/gfx/panel.h
@@ -35,19 +35,11 @@
#ifndef SWORD25_PANEL_H
#define SWORD25_PANEL_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/gfx/renderobject.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Class Definition
-// -----------------------------------------------------------------------------
-
class Panel : public RenderObject {
friend class RenderObject;
@@ -58,10 +50,10 @@ private:
public:
virtual ~Panel();
- uint getColor() const {
+ uint getColor() const {
return _color;
}
- void setColor(uint color) {
+ void setColor(uint color) {
_color = color;
forceRefresh();
}
diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp
index 5de6dde79e..77af0bee92 100644
--- a/engines/sword25/gfx/renderobject.cpp
+++ b/engines/sword25/gfx/renderobject.cpp
@@ -53,8 +53,6 @@ namespace Sword25 {
#define BS_LOG_PREFIX "RENDEROBJECT"
-// Konstruktion / Destruktion
-// --------------------------
RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
_managerPtr(0),
_parentPtr(parentPtr),
@@ -76,9 +74,9 @@ RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type,
// Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle.
if (handle == 0)
- _handle = RenderObjectRegistry::getInstance().registerObject(this);
+ _handle = RenderObjectRegistry::instance().registerObject(this);
else
- _handle = RenderObjectRegistry::getInstance().registerObject(this, handle);
+ _handle = RenderObjectRegistry::instance().registerObject(this, handle);
if (_handle == 0)
return;
@@ -110,11 +108,9 @@ RenderObject::~RenderObject() {
deleteAllChildren();
// Objekt deregistrieren.
- RenderObjectRegistry::getInstance().deregisterObject(this);
+ RenderObjectRegistry::instance().deregisterObject(this);
}
-// Rendern
-// -------
bool RenderObject::render() {
// Objektänderungen validieren
validateObject();
@@ -141,9 +137,6 @@ bool RenderObject::render() {
return true;
}
-// Objektverwaltung
-// ----------------
-
void RenderObject::validateObject() {
// Die Veränderungen in den Objektvariablen aufheben
_oldBbox = _bbox;
@@ -222,9 +215,6 @@ int RenderObject::calcAbsoluteY() const {
return _y;
}
-// Baumverwaltung
-// --------------
-
void RenderObject::deleteAllChildren() {
// Es ist nicht notwendig die Liste zu iterieren, da jedes Kind für sich DetatchChildren an diesem Objekt aufruft und sich somit
// selber entfernt. Daher muss immer nur ein beliebiges Element (hier das letzte) gelöscht werden, bis die Liste leer ist.
@@ -275,17 +265,12 @@ void RenderObject::updateAbsolutePos() {
(*it)->updateAbsolutePos();
}
-// Get-Methoden
-// ------------
-
bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) {
result = pObject->getBbox();
result.clip(_bbox);
return result.isValidRect();
}
-// Set-Methoden
-// ------------
void RenderObject::setPos(int x, int y) {
_x = x;
_y = y;
@@ -313,10 +298,6 @@ void RenderObject::setVisible(bool visible) {
_visible = visible;
}
-// -----------------------------------------------------------------------------
-// Objekterzeuger
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<Animation> RenderObject::addAnimation(const Common::String &filename) {
RenderObjectPtr<Animation> aniPtr((new Animation(this->getHandle(), filename))->getHandle());
if (aniPtr.isValid() && aniPtr->getInitSuccess())
@@ -328,9 +309,6 @@ RenderObjectPtr<Animation> RenderObject::addAnimation(const Common::String &file
}
}
-
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<Animation> RenderObject::addAnimation(const AnimationTemplate &animationTemplate) {
Animation *aniPtr = new Animation(this->getHandle(), animationTemplate);
if (aniPtr && aniPtr->getInitSuccess())
@@ -341,8 +319,6 @@ RenderObjectPtr<Animation> RenderObject::addAnimation(const AnimationTemplate &a
}
}
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<Bitmap> RenderObject::addBitmap(const Common::String &filename) {
RenderObjectPtr<Bitmap> bitmapPtr((new StaticBitmap(this->getHandle(), filename))->getHandle());
if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
@@ -354,8 +330,6 @@ RenderObjectPtr<Bitmap> RenderObject::addBitmap(const Common::String &filename)
}
}
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<Bitmap> RenderObject::addDynamicBitmap(uint width, uint height) {
RenderObjectPtr<Bitmap> bitmapPtr((new DynamicBitmap(this->getHandle(), width, height))->getHandle());
if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
@@ -367,8 +341,6 @@ RenderObjectPtr<Bitmap> RenderObject::addDynamicBitmap(uint width, uint height)
}
}
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<Panel> RenderObject::addPanel(int width, int height, uint color) {
RenderObjectPtr<Panel> panelPtr((new Panel(this->getHandle(), width, height, color))->getHandle());
if (panelPtr.isValid() && panelPtr->getInitSuccess())
@@ -380,12 +352,10 @@ RenderObjectPtr<Panel> RenderObject::addPanel(int width, int height, uint color)
}
}
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<Text> RenderObject::addText(const Common::String &font, const Common::String &text) {
RenderObjectPtr<Text> textPtr((new Text(this->getHandle()))->getHandle());
- if (textPtr.isValid() && textPtr->getInitSuccess() && textPtr->SetFont(font)) {
- textPtr->SetText(text);
+ if (textPtr.isValid() && textPtr->getInitSuccess() && textPtr->setFont(font)) {
+ textPtr->setText(text);
return textPtr;
} else {
if (textPtr.isValid())
@@ -394,9 +364,6 @@ RenderObjectPtr<Text> RenderObject::addText(const Common::String &font, const Co
}
}
-// Persistenz-Methoden
-// -------------------
-
bool RenderObject::persist(OutputPersistenceBlock &writer) {
// Typ und Handle werden als erstes gespeichert, damit beim Laden ein Objekt vom richtigen Typ mit dem richtigen Handle erzeugt werden kann.
writer.write(static_cast<uint>(_type));
@@ -431,8 +398,6 @@ bool RenderObject::persist(OutputPersistenceBlock &writer) {
return true;
}
-// -----------------------------------------------------------------------------
-
bool RenderObject::unpersist(InputPersistenceBlock &reader) {
// Typ und Handle wurden schon von RecreatePersistedRenderObject() ausgelesen. Jetzt werden die restlichen Objekteigenschaften ausgelesen.
reader.read(_x);
@@ -468,8 +433,6 @@ bool RenderObject::unpersist(InputPersistenceBlock &reader) {
return reader.isGood();
}
-// -----------------------------------------------------------------------------
-
bool RenderObject::persistChildren(OutputPersistenceBlock &writer) {
bool result = true;
@@ -486,8 +449,6 @@ bool RenderObject::persistChildren(OutputPersistenceBlock &writer) {
return result;
}
-// -----------------------------------------------------------------------------
-
bool RenderObject::unpersistChildren(InputPersistenceBlock &reader) {
bool result = true;
@@ -506,8 +467,6 @@ bool RenderObject::unpersistChildren(InputPersistenceBlock &reader) {
return result && reader.isGood();
}
-// -----------------------------------------------------------------------------
-
RenderObjectPtr<RenderObject> RenderObject::recreatePersistedRenderObject(InputPersistenceBlock &reader) {
RenderObjectPtr<RenderObject> result;
@@ -547,8 +506,6 @@ RenderObjectPtr<RenderObject> RenderObject::recreatePersistedRenderObject(InputP
return result;
}
-// Hilfs-Methoden
-// --------------
bool RenderObject::greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs) {
// Das Objekt mit dem kleinem Z-Wert müssen zuerst gerendert werden.
if (lhs->_z != rhs->_z)
diff --git a/engines/sword25/gfx/renderobject.h b/engines/sword25/gfx/renderobject.h
index c090ad75c9..7b7b9047f7 100644
--- a/engines/sword25/gfx/renderobject.h
+++ b/engines/sword25/gfx/renderobject.h
@@ -45,7 +45,6 @@
#ifndef SWORD25_RENDEROBJECT_H
#define SWORD25_RENDEROBJECT_H
-// Includes
#include "sword25/kernel/common.h"
#include "sword25/kernel/persistable.h"
#include "common/rect.h"
@@ -55,10 +54,6 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Forward Declarations
-// -----------------------------------------------------------------------------
-
class Kernel;
class RenderObjectManager;
class Bitmap;
@@ -352,7 +347,7 @@ public:
*/
void forceRefresh() {
_refreshForced = true;
- };
+ }
/**
@brief Gibt das Handle des Objekte zurück.
*/
@@ -462,11 +457,11 @@ private:
@param pObject ein Pointer auf das zu entfernende Objekt
@return Gibt false zurück, falls das zu entfernende Objekt nicht in der Liste gefunden werden konnte.
*/
- bool detatchChildren(RenderObjectPtr<RenderObject> pObject);
+ bool detatchChildren(RenderObjectPtr<RenderObject> pObject);
/**
@brief Berechnet die Bounding-Box und registriert das Dirty-Rect beim BS_RenderObjectManager.
*/
- void updateBoxes();
+ void updateBoxes();
/**
@brief Berechnet die Bounding-Box des Objektes.
@return Gibt die Bounding-Box des Objektes in Bildschirmkoordinaten zurück.
@@ -496,7 +491,7 @@ private:
/**
@brief Validiert den Zustand eines Objektes nachdem die durch die Veränderung verursachten Folgen abgearbeitet wurden.
*/
- void validateObject();
+ void validateObject();
/**
@brief Berechnet die absolute Position des Objektes und aller seiner Kinderobjekte neu.
@@ -508,7 +503,7 @@ private:
@brief Teilt dem Objekt mit, dass sich eines seiner Kinderobjekte dahingehend verändert hat, die eine erneute Bestimmung der
Rendereihenfolge verlangt.
*/
- void signalChildChange() {
+ void signalChildChange() {
_childChanged = true;
}
/**
@@ -517,7 +512,7 @@ private:
@param Result das Ergebnisrechteck
@return Gibt false zurück, falls sich die Objekte gar nicht schneiden.
*/
- bool getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result);
+ bool getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result);
/**
@brief Vergleichsoperator der auf Objektpointern basiert statt auf Objekten.
@remark Diese Methode wird fürs Sortieren der Kinderliste nach der Rendereihenfolge benutzt.
diff --git a/engines/sword25/gfx/renderobjectmanager.cpp b/engines/sword25/gfx/renderobjectmanager.cpp
index ab4e606270..94f7a04b45 100644
--- a/engines/sword25/gfx/renderobjectmanager.cpp
+++ b/engines/sword25/gfx/renderobjectmanager.cpp
@@ -63,7 +63,7 @@ void RenderObjectManager::startFrame() {
_frameStarted = true;
// Verstrichene Zeit bestimmen
- int timeElapsed = Kernel::GetInstance()->GetGfx()->GetLastFrameDurationMicro();
+ int timeElapsed = Kernel::getInstance()->getGfx()->getLastFrameDurationMicro();
// Alle BS_TimedRenderObject Objekte über den Framestart und die verstrichene Zeit in Kenntnis setzen
RenderObjectList::iterator iter = _timedRenderObjects.begin();
@@ -113,7 +113,7 @@ bool RenderObjectManager::persist(OutputPersistenceBlock &writer) {
}
// Alle BS_AnimationTemplates persistieren.
- result &= AnimationTemplateRegistry::getInstance().persist(writer);
+ result &= AnimationTemplateRegistry::instance().persist(writer);
return result;
}
@@ -143,7 +143,7 @@ bool RenderObjectManager::unpersist(InputPersistenceBlock &reader) {
}
// Alle BS_AnimationTemplates wieder herstellen.
- result &= AnimationTemplateRegistry::getInstance().unpersist(reader);
+ result &= AnimationTemplateRegistry::instance().unpersist(reader);
return result;
}
diff --git a/engines/sword25/gfx/renderobjectmanager.h b/engines/sword25/gfx/renderobjectmanager.h
index 05bba37cd0..8511382d6e 100644
--- a/engines/sword25/gfx/renderobjectmanager.h
+++ b/engines/sword25/gfx/renderobjectmanager.h
@@ -45,7 +45,6 @@
#ifndef SWORD25_RENDEROBJECTMANAGER_H
#define SWORD25_RENDEROBJECTMANAGER_H
-// Includes
#include "common/rect.h"
#include "sword25/kernel/common.h"
#include "sword25/gfx/renderobjectptr.h"
@@ -53,7 +52,6 @@
namespace Sword25 {
-// Klassendefinition
class Kernel;
class RenderObject;
class TimedRenderObject;
diff --git a/engines/sword25/gfx/renderobjectptr.h b/engines/sword25/gfx/renderobjectptr.h
index 894ba877d2..c22c6e83e7 100644
--- a/engines/sword25/gfx/renderobjectptr.h
+++ b/engines/sword25/gfx/renderobjectptr.h
@@ -54,7 +54,7 @@ public:
RenderObjectPtr(uint handle) : _handle(handle) {}
T *operator->() const {
- return static_cast<T *>(RenderObjectRegistry::getInstance().resolveHandle(_handle));
+ return static_cast<T *>(RenderObjectRegistry::instance().resolveHandle(_handle));
}
bool operator==(const RenderObjectPtr<T> & other) {
@@ -62,11 +62,11 @@ public:
}
bool isValid() const {
- return RenderObjectRegistry::getInstance().resolveHandle(_handle) != 0;
+ return RenderObjectRegistry::instance().resolveHandle(_handle) != 0;
}
void erase() {
- delete static_cast<T *>(RenderObjectRegistry::getInstance().resolveHandle(_handle));
+ delete static_cast<T *>(RenderObjectRegistry::instance().resolveHandle(_handle));
_handle = 0;
}
diff --git a/engines/sword25/gfx/renderobjectregistry.cpp b/engines/sword25/gfx/renderobjectregistry.cpp
index edfdbd23a8..8f55cf65a9 100644
--- a/engines/sword25/gfx/renderobjectregistry.cpp
+++ b/engines/sword25/gfx/renderobjectregistry.cpp
@@ -34,14 +34,12 @@
#include "sword25/gfx/renderobjectregistry.h"
-#include "common/ptr.h"
+DECLARE_SINGLETON(Sword25::RenderObjectRegistry);
namespace Sword25 {
#define BS_LOG_PREFIX "RENDEROBJECTREGISTRY"
-Common::ScopedPtr<RenderObjectRegistry> RenderObjectRegistry::_instancePtr;
-
void RenderObjectRegistry::logErrorLn(const char *message) const {
BS_LOG_ERRORLN(message);
}
diff --git a/engines/sword25/gfx/renderobjectregistry.h b/engines/sword25/gfx/renderobjectregistry.h
index b546ee56e1..357d041068 100644
--- a/engines/sword25/gfx/renderobjectregistry.h
+++ b/engines/sword25/gfx/renderobjectregistry.h
@@ -35,42 +35,21 @@
#ifndef SWORD25_RENDEROBJECTREGISTRY_H
#define SWORD25_RENDEROBJECTREGISTRY_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/kernel/objectregistry.h"
-#include "common/ptr.h"
+#include "common/singleton.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Forward Deklarationen
-// -----------------------------------------------------------------------------
-
class RenderObject;
-// -----------------------------------------------------------------------------
-// Klassendeklaration
-// -----------------------------------------------------------------------------
-
-class RenderObjectRegistry : public ObjectRegistry<RenderObject> {
-public:
- static RenderObjectRegistry &getInstance() {
- if (!_instancePtr.get())
- _instancePtr.reset(new RenderObjectRegistry);
- return *_instancePtr.get();
- }
-
- virtual ~RenderObjectRegistry() {}
-
+class RenderObjectRegistry :
+ public ObjectRegistry<RenderObject>,
+ public Common::Singleton<RenderObjectRegistry> {
private:
virtual void logErrorLn(const char *message) const;
virtual void logWarningLn(const char *message) const;
-
- static Common::ScopedPtr<RenderObjectRegistry> _instancePtr;
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/screenshot.cpp b/engines/sword25/gfx/screenshot.cpp
index 9eea2ec422..b7bb31c8e4 100644
--- a/engines/sword25/gfx/screenshot.cpp
+++ b/engines/sword25/gfx/screenshot.cpp
@@ -32,53 +32,48 @@
*
*/
-#define BS_LOG_PREFIX "SCREENSHOT"
+// Disable symbol overrides so that we can use png.h
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
+#define BS_LOG_PREFIX "SCREENSHOT"
-#include "common/system.h"
-#include "common/savefile.h"
+#include "common/memstream.h"
#include "sword25/gfx/screenshot.h"
#include "sword25/kernel/filesystemutil.h"
#include <png.h>
namespace Sword25 {
-// -----------------------------------------------------------------------------
-
#include "common/pack-start.h"
struct RGB_PIXEL {
- byte Red;
- byte Green;
- byte Blue;
+ byte red;
+ byte green;
+ byte blue;
} PACKED_STRUCT;
#include "common/pack-end.h"
void userWriteFn(png_structp png_ptr, png_bytep data, png_size_t length) {
- static_cast<Common::WriteStream *>(png_ptr->io_ptr)->write(data, length);
+ static_cast<Common::WriteStream *>(png_get_io_ptr(png_ptr))->write(data, length);
}
void userFlushFn(png_structp png_ptr) {
}
-
-bool Screenshot::SaveToFile(Graphics::Surface *Data, Common::WriteStream *Stream) {
+bool Screenshot::saveToFile(Graphics::Surface *data, Common::WriteStream *stream) {
// Reserve buffer space
- RGB_PIXEL *pixelBuffer = new RGB_PIXEL[Data->w * Data->h];
+ RGB_PIXEL *pixelBuffer = new RGB_PIXEL[data->w * data->h];
// Convert the RGBA data to RGB
- const byte *pSrc = (const byte *)Data->getBasePtr(0, 0);
+ const byte *pSrc = (const byte *)data->getBasePtr(0, 0);
RGB_PIXEL *pDest = pixelBuffer;
- for (uint y = 0; y < Data->h; y++) {
- for (uint x = 0; x < Data->w; x++) {
+ for (uint y = 0; y < data->h; y++) {
+ for (uint x = 0; x < data->w; x++) {
uint32 srcPixel = READ_LE_UINT32(pSrc);
pSrc += sizeof(uint32);
- pDest->Red = (srcPixel >> 16) & 0xff;
- pDest->Green = (srcPixel >> 8) & 0xff;
- pDest->Blue = srcPixel & 0xff;
+ pDest->red = (srcPixel >> 16) & 0xff;
+ pDest->green = (srcPixel >> 8) & 0xff;
+ pDest->blue = srcPixel & 0xff;
++pDest;
}
}
@@ -91,30 +86,30 @@ bool Screenshot::SaveToFile(Graphics::Surface *Data, Common::WriteStream *Stream
if (!info_ptr)
error("Could not create PNG info-struct.");
- // The compression buffer must be large enough to the entire image.
+ // The compression buffer must be large enough to the entire image.
// This ensures that only an IDAT chunk is created.
// When buffer size is used 110% of the raw data size to be sure.
- png_set_compression_buffer_size(png_ptr, (Data->w * Data->h * 3 * 110) / 100);
+ png_set_compression_buffer_size(png_ptr, (data->w * data->h * 3 * 110) / 100);
// Initialise PNG-Info structure
png_set_IHDR(png_ptr, info_ptr,
- Data->w, // Width
- Data->h, // Height
- 8, // Bits depth
- PNG_COLOR_TYPE_RGB, // Colour type
+ data->w, // Width
+ data->h, // Height
+ 8, // Bits depth
+ PNG_COLOR_TYPE_RGB, // Color type
PNG_INTERLACE_NONE, // No interlacing
PNG_COMPRESSION_TYPE_DEFAULT, // Compression type
PNG_FILTER_TYPE_DEFAULT); // Filter Type
// Rowpointer erstellen
- png_bytep *rowPointers = new png_bytep[Data->h];
- for (uint i = 0; i < Data->h; i++) {
- rowPointers[i] = (png_bytep)&pixelBuffer[Data->w * i];
+ png_bytep *rowPointers = new png_bytep[data->h];
+ for (uint i = 0; i < data->h; i++) {
+ rowPointers[i] = (png_bytep)&pixelBuffer[data->w * i];
}
png_set_rows(png_ptr, info_ptr, &rowPointers[0]);
// Write out the png data to the file
- png_set_write_fn(png_ptr, (void *)Stream, userWriteFn, userFlushFn);
+ png_set_write_fn(png_ptr, (void *)stream, userWriteFn, userFlushFn);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
@@ -127,14 +122,14 @@ bool Screenshot::SaveToFile(Graphics::Surface *Data, Common::WriteStream *Stream
// -----------------------------------------------------------------------------
-Common::MemoryReadStream *Screenshot::createThumbnail(Graphics::Surface *Data) {
+Common::SeekableReadStream *Screenshot::createThumbnail(Graphics::Surface *data) {
// This method takes a screen image with a dimension of 800x600, and creates a screenshot with a dimension of 200x125.
// First 50 pixels are cut off the top and bottom (the interface boards in the game). The remaining image of 800x500
// will be on a 16th of its size, reduced by being handed out in 4x4 pixel blocks and the average of each block
// generates a pixel of the target image. Finally, the result as a PNG file is stored as a file.
// The source image must be 800x600.
- if (Data->w != 800 || Data->h != 600 || Data->bytesPerPixel != 4) {
+ if (data->w != 800 || data->h != 600 || data->bytesPerPixel != 4) {
BS_LOG_ERRORLN("The sreenshot dimensions have to be 800x600 in order to be saved as a thumbnail.");
return false;
}
@@ -152,7 +147,7 @@ Common::MemoryReadStream *Screenshot::createThumbnail(Graphics::Surface *Data) {
int alpha, red, green, blue;
alpha = red = green = blue = 0;
for (int j = 0; j < 4; ++j) {
- const uint32 *srcP = (const uint32 *)Data->getBasePtr(x * 4, y * 4 + j + 50);
+ const uint32 *srcP = (const uint32 *)data->getBasePtr(x * 4, y * 4 + j + 50);
for (int i = 0; i < 4; ++i) {
uint32 pixel = READ_LE_UINT32(srcP + i);
alpha += (pixel >> 24);
@@ -178,10 +173,10 @@ Common::MemoryReadStream *Screenshot::createThumbnail(Graphics::Surface *Data) {
// Create a PNG representation of the thumbnail data
Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
- SaveToFile(&thumbnail, stream);
+ saveToFile(&thumbnail, stream);
// Output a MemoryReadStream that encompasses the written data
- Common::MemoryReadStream *result = new Common::MemoryReadStream(stream->getData(), stream->size(),
+ Common::SeekableReadStream *result = new Common::MemoryReadStream(stream->getData(), stream->size(),
DisposeAfterUse::YES);
return result;
}
diff --git a/engines/sword25/gfx/screenshot.h b/engines/sword25/gfx/screenshot.h
index d328130b3f..a0f615f9e6 100644
--- a/engines/sword25/gfx/screenshot.h
+++ b/engines/sword25/gfx/screenshot.h
@@ -35,23 +35,15 @@
#ifndef SWORD25_SCREENSHOT_H
#define SWORD25_SCREENSHOT_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "graphics/surface.h"
#include "sword25/kernel/common.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Class declaration
-// -----------------------------------------------------------------------------
-
class Screenshot {
public:
- static bool SaveToFile(Graphics::Surface *Data, Common::WriteStream *Stream);
- static Common::MemoryReadStream *createThumbnail(Graphics::Surface *Data);
+ static bool saveToFile(Graphics::Surface *data, Common::WriteStream *stream);
+ static Common::SeekableReadStream *createThumbnail(Graphics::Surface *data);
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/staticbitmap.cpp b/engines/sword25/gfx/staticbitmap.cpp
index 7771eb8100..3019fe0ac1 100644
--- a/engines/sword25/gfx/staticbitmap.cpp
+++ b/engines/sword25/gfx/staticbitmap.cpp
@@ -32,10 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/gfx/staticbitmap.h"
#include "sword25/gfx/bitmapresource.h"
#include "sword25/package/packagemanager.h"
@@ -44,16 +40,8 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Logging
-// -----------------------------------------------------------------------------
-
#define BS_LOG_PREFIX "STATICBITMAP"
-// -----------------------------------------------------------------------------
-// Konstruktion / Destruktion
-// -----------------------------------------------------------------------------
-
StaticBitmap::StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename) :
Bitmap(parentPtr, TYPE_STATICBITMAP) {
// Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
@@ -63,23 +51,19 @@ StaticBitmap::StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common
_initSuccess = initBitmapResource(filename);
}
-// -----------------------------------------------------------------------------
-
StaticBitmap::StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
Bitmap(parentPtr, TYPE_STATICBITMAP, handle) {
_initSuccess = unpersist(reader);
}
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::initBitmapResource(const Common::String &filename) {
// Bild-Resource laden
- Resource *resourcePtr = Kernel::GetInstance()->GetResourceManager()->RequestResource(filename);
+ Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(filename);
if (!resourcePtr) {
BS_LOG_ERRORLN("Could not request resource \"%s\".", filename.c_str());
return false;
}
- if (resourcePtr->GetType() != Resource::TYPE_BITMAP) {
+ if (resourcePtr->getType() != Resource::TYPE_BITMAP) {
BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", filename.c_str());
return false;
}
@@ -99,22 +83,18 @@ bool StaticBitmap::initBitmapResource(const Common::String &filename) {
return true;
}
-// -----------------------------------------------------------------------------
-
StaticBitmap::~StaticBitmap() {
}
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::doRender() {
// Bitmap holen
- Resource *resourcePtr = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
+ Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
BS_ASSERT(resourcePtr);
- BS_ASSERT(resourcePtr->GetType() == Resource::TYPE_BITMAP);
+ BS_ASSERT(resourcePtr->getType() == Resource::TYPE_BITMAP);
BitmapResource *bitmapResourcePtr = static_cast<BitmapResource *>(resourcePtr);
// Framebufferobjekt holen
- GraphicEngine *gfxPtr = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
BS_ASSERT(gfxPtr);
// Bitmap zeichnen
@@ -137,68 +117,52 @@ bool StaticBitmap::doRender() {
return result;
}
-// -----------------------------------------------------------------------------
-
uint StaticBitmap::getPixel(int x, int y) const {
BS_ASSERT(x >= 0 && x < _width);
BS_ASSERT(y >= 0 && y < _height);
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
uint result = pBitmapResource->getPixel(x, y);
pResource->release();
return result;
}
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
BS_LOG_ERRORLN("SetContent() ist not supported with this object.");
return false;
}
-// -----------------------------------------------------------------------------
-// Auskunftsmethoden
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::isAlphaAllowed() const {
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
bool result = static_cast<BitmapResource *>(pResource)->isAlphaAllowed();
pResource->release();
return result;
}
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::isColorModulationAllowed() const {
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
bool result = static_cast<BitmapResource *>(pResource)->isColorModulationAllowed();
pResource->release();
return result;
}
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::isScalingAllowed() const {
- Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
- BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
+ BS_ASSERT(pResource->getType() == Resource::TYPE_BITMAP);
bool result = static_cast<BitmapResource *>(pResource)->isScalingAllowed();
pResource->release();
return result;
}
-// -----------------------------------------------------------------------------
-// Persistenz
-// -----------------------------------------------------------------------------
-
bool StaticBitmap::persist(OutputPersistenceBlock &writer) {
bool result = true;
result &= Bitmap::persist(writer);
- writer.write(_resourceFilename);
+ writer.writeString(_resourceFilename);
result &= RenderObject::persistChildren(writer);
@@ -210,7 +174,7 @@ bool StaticBitmap::unpersist(InputPersistenceBlock &reader) {
result &= Bitmap::unpersist(reader);
Common::String resourceFilename;
- reader.read(resourceFilename);
+ reader.readString(resourceFilename);
result &= initBitmapResource(resourceFilename);
result &= RenderObject::unpersistChildren(reader);
diff --git a/engines/sword25/gfx/staticbitmap.h b/engines/sword25/gfx/staticbitmap.h
index a325487213..b5b4c4f5a2 100644
--- a/engines/sword25/gfx/staticbitmap.h
+++ b/engines/sword25/gfx/staticbitmap.h
@@ -35,19 +35,11 @@
#ifndef SWORD25_STATIC_BITMAP_H
#define SWORD25_STATIC_BITMAP_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/gfx/bitmap.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Klassendeklaration
-// -----------------------------------------------------------------------------
-
class StaticBitmap : public Bitmap {
friend class RenderObject;
@@ -63,20 +55,20 @@ public:
virtual uint getPixel(int x, int y) const;
- virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
- virtual bool isScalingAllowed() const;
- virtual bool isAlphaAllowed() const;
- virtual bool isColorModulationAllowed() const;
- virtual bool isSetContentAllowed() const {
+ virtual bool isScalingAllowed() const;
+ virtual bool isAlphaAllowed() const;
+ virtual bool isColorModulationAllowed() const;
+ virtual bool isSetContentAllowed() const {
return false;
}
- virtual bool persist(OutputPersistenceBlock &writer);
- virtual bool unpersist(InputPersistenceBlock &reader);
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
protected:
- virtual bool doRender();
+ virtual bool doRender();
private:
Common::String _resourceFilename;
diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp
index 1b0c3a78f0..b8e12ba570 100644
--- a/engines/sword25/gfx/text.cpp
+++ b/engines/sword25/gfx/text.cpp
@@ -36,10 +36,6 @@
// Entweder Fontfile absolut abspeichern, oder Verzeichniswechseln verbieten
// Eine relative Fontfile-Angabe könnte verwandt werden nachdem das Verzeichnis bereits gewechselt wurde und die Datei würde nicht mehr gefunden
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/kernel.h"
#include "sword25/kernel/outputpersistenceblock.h"
#include "sword25/kernel/inputpersistenceblock.h"
@@ -52,59 +48,49 @@ namespace Sword25 {
#define BS_LOG_PREFIX "TEXT"
-// -----------------------------------------------------------------------------
-// Konstanten
-// -----------------------------------------------------------------------------
-
namespace {
const uint AUTO_WRAP_THRESHOLD_DEFAULT = 300;
}
-// -----------------------------------------------------------------------------
-// Konstruktion / Destruktion
-// -----------------------------------------------------------------------------
-
-Text::Text(RenderObjectPtr<RenderObject> ParentPtr) :
- RenderObject(ParentPtr, RenderObject::TYPE_TEXT),
+Text::Text(RenderObjectPtr<RenderObject> parentPtr) :
+ RenderObject(parentPtr, RenderObject::TYPE_TEXT),
_modulationColor(0xffffffff),
- m_AutoWrap(false),
- m_AutoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
+ _autoWrap(false),
+ _autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
}
-// -----------------------------------------------------------------------------
+Text::Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ RenderObject(parentPtr, TYPE_TEXT, handle),
+ // Temporarily set fields prior to unpersisting actual values
+ _modulationColor(0xffffffff),
+ _autoWrap(false),
+ _autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
-Text::Text(InputPersistenceBlock &Reader, RenderObjectPtr<RenderObject> ParentPtr, uint Handle) :
- RenderObject(ParentPtr, TYPE_TEXT, Handle) {
- _initSuccess = unpersist(Reader);
+ // Unpersist the fields
+ _initSuccess = unpersist(reader);
}
-// -----------------------------------------------------------------------------
-
-bool Text::SetFont(const Common::String &Font) {
+bool Text::setFont(const Common::String &font) {
// Font precachen.
- if (GetResourceManager()->PrecacheResource(Font)) {
- m_Font = Font;
- UpdateFormat();
+ if (getResourceManager()->precacheResource(font)) {
+ _font = font;
+ updateFormat();
forceRefresh();
return true;
} else {
- BS_LOG_ERRORLN("Could not precache font \"%s\". Font probably does not exist.", Font.c_str());
+ BS_LOG_ERRORLN("Could not precache font \"%s\". Font probably does not exist.", font.c_str());
return false;
}
}
-// -----------------------------------------------------------------------------
-
-void Text::SetText(const Common::String &text) {
- m_Text = text;
- UpdateFormat();
+void Text::setText(const Common::String &text) {
+ _text = text;
+ updateFormat();
forceRefresh();
}
-// -----------------------------------------------------------------------------
-
void Text::setColor(uint modulationColor) {
uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
if (newModulationColor != _modulationColor) {
@@ -113,8 +99,6 @@ void Text::setColor(uint modulationColor) {
}
}
-// -----------------------------------------------------------------------------
-
void Text::setAlpha(int alpha) {
BS_ASSERT(alpha >= 0 && alpha < 256);
uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
@@ -124,225 +108,215 @@ void Text::setAlpha(int alpha) {
}
}
-// -----------------------------------------------------------------------------
-
-void Text::SetAutoWrap(bool AutoWrap) {
- if (AutoWrap != m_AutoWrap) {
- m_AutoWrap = AutoWrap;
- UpdateFormat();
+void Text::setAutoWrap(bool autoWrap) {
+ if (autoWrap != _autoWrap) {
+ _autoWrap = autoWrap;
+ updateFormat();
forceRefresh();
}
}
-// -----------------------------------------------------------------------------
-
-void Text::SetAutoWrapThreshold(uint AutoWrapThreshold) {
- if (AutoWrapThreshold != m_AutoWrapThreshold) {
- m_AutoWrapThreshold = AutoWrapThreshold;
- UpdateFormat();
+void Text::setAutoWrapThreshold(uint autoWrapThreshold) {
+ if (autoWrapThreshold != _autoWrapThreshold) {
+ _autoWrapThreshold = autoWrapThreshold;
+ updateFormat();
forceRefresh();
}
}
-// -----------------------------------------------------------------------------
-
bool Text::doRender() {
// Font-Resource locken.
- FontResource *FontPtr = LockFontResource();
- if (!FontPtr) return false;
+ FontResource *fontPtr = lockFontResource();
+ if (!fontPtr)
+ return false;
// Charactermap-Resource locken.
- ResourceManager *RMPtr = GetResourceManager();
- BitmapResource *CharMapPtr;
+ ResourceManager *rmPtr = getResourceManager();
+ BitmapResource *charMapPtr;
{
- Resource *pResource = RMPtr->RequestResource(FontPtr->GetCharactermapFileName());
+ Resource *pResource = rmPtr->requestResource(fontPtr->getCharactermapFileName());
if (!pResource) {
- BS_LOG_ERRORLN("Could not request resource \"%s\".", FontPtr->GetCharactermapFileName().c_str());
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", fontPtr->getCharactermapFileName().c_str());
return false;
}
- if (pResource->GetType() != Resource::TYPE_BITMAP) {
- BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", FontPtr->GetCharactermapFileName().c_str());
+ if (pResource->getType() != Resource::TYPE_BITMAP) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", fontPtr->getCharactermapFileName().c_str());
return false;
}
- CharMapPtr = static_cast<BitmapResource *>(pResource);
+ charMapPtr = static_cast<BitmapResource *>(pResource);
}
// Framebufferobjekt holen.
- GraphicEngine *GfxPtr = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
- BS_ASSERT(GfxPtr);
+ GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
+ BS_ASSERT(gfxPtr);
- bool Result = true;
- Common::Array<LINE>::iterator Iter = m_Lines.begin();
- for (; Iter != m_Lines.end(); ++Iter) {
+ bool result = true;
+ Common::Array<Line>::iterator iter = _lines.begin();
+ for (; iter != _lines.end(); ++iter) {
// Feststellen, ob überhaupt Buchstaben der aktuellen Zeile vom Update betroffen sind.
- Common::Rect CheckRect = (*Iter).BBox;
- CheckRect.translate(_absoluteX, _absoluteY);
+ Common::Rect checkRect = (*iter).bbox;
+ checkRect.translate(_absoluteX, _absoluteY);
// Jeden Buchstaben einzeln Rendern.
- int CurX = _absoluteX + (*Iter).BBox.left;
- int CurY = _absoluteY + (*Iter).BBox.top;
- for (uint i = 0; i < (*Iter).Text.size(); ++i) {
- Common::Rect CurRect = FontPtr->GetCharacterRect((byte)(*Iter).Text[i]);
-
- Common::Rect RenderRect(CurX, CurY, CurX + CurRect.width(), CurY + CurRect.height());
- int RenderX = CurX + (RenderRect.left - RenderRect.left);
- int RenderY = CurY + (RenderRect.top - RenderRect.top);
- RenderRect.translate(CurRect.left - CurX, CurRect.top - CurY);
- Result = CharMapPtr->blit(RenderX, RenderY, Image::FLIP_NONE, &RenderRect, _modulationColor);
- if (!Result) break;
-
- CurX += CurRect.width() + FontPtr->GetGapWidth();
+ int curX = _absoluteX + (*iter).bbox.left;
+ int curY = _absoluteY + (*iter).bbox.top;
+ for (uint i = 0; i < (*iter).text.size(); ++i) {
+ Common::Rect curRect = fontPtr->getCharacterRect((byte)(*iter).text[i]);
+
+ Common::Rect renderRect(curX, curY, curX + curRect.width(), curY + curRect.height());
+ int renderX = curX + (renderRect.left - renderRect.left);
+ int renderY = curY + (renderRect.top - renderRect.top);
+ renderRect.translate(curRect.left - curX, curRect.top - curY);
+ result = charMapPtr->blit(renderX, renderY, Image::FLIP_NONE, &renderRect, _modulationColor);
+ if (!result)
+ break;
+
+ curX += curRect.width() + fontPtr->getGapWidth();
}
}
// Charactermap-Resource freigeben.
- CharMapPtr->release();
+ charMapPtr->release();
// Font-Resource freigeben.
- FontPtr->release();
+ fontPtr->release();
- return Result;
+ return result;
}
-// -----------------------------------------------------------------------------
-
-ResourceManager *Text::GetResourceManager() {
+ResourceManager *Text::getResourceManager() {
// Pointer auf den Resource-Manager holen.
- return Kernel::GetInstance()->GetResourceManager();
+ return Kernel::getInstance()->getResourceManager();
}
-// -----------------------------------------------------------------------------
-
-FontResource *Text::LockFontResource() {
- ResourceManager *RMPtr = GetResourceManager();
+FontResource *Text::lockFontResource() {
+ ResourceManager *rmPtr = getResourceManager();
// Font-Resource locken.
- FontResource *FontPtr;
+ FontResource *fontPtr;
{
- Resource *ResourcePtr = RMPtr->RequestResource(m_Font);
- if (!ResourcePtr) {
- BS_LOG_ERRORLN("Could not request resource \"%s\".", m_Font.c_str());
+ Resource *resourcePtr = rmPtr->requestResource(_font);
+ if (!resourcePtr) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", _font.c_str());
return NULL;
}
- if (ResourcePtr->GetType() != Resource::TYPE_FONT) {
- BS_LOG_ERRORLN("Requested resource \"%s\" is not a font.", m_Font.c_str());
+ if (resourcePtr->getType() != Resource::TYPE_FONT) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a font.", _font.c_str());
return NULL;
}
- FontPtr = static_cast<FontResource *>(ResourcePtr);
+ fontPtr = static_cast<FontResource *>(resourcePtr);
}
- return FontPtr;
+ return fontPtr;
}
-// -----------------------------------------------------------------------------
-
-void Text::UpdateFormat() {
- FontResource *FontPtr = LockFontResource();
- BS_ASSERT(FontPtr);
+void Text::updateFormat() {
+ FontResource *fontPtr = lockFontResource();
+ BS_ASSERT(fontPtr);
- UpdateMetrics(*FontPtr);
+ updateMetrics(*fontPtr);
- m_Lines.resize(1);
- if (m_AutoWrap && (uint) _width >= m_AutoWrapThreshold && m_Text.size() >= 2) {
+ _lines.resize(1);
+ if (_autoWrap && (uint) _width >= _autoWrapThreshold && _text.size() >= 2) {
_width = 0;
- uint CurLineWidth = 0;
- uint CurLineHeight = 0;
- uint CurLine = 0;
- uint TempLineWidth = 0;
- uint LastSpace = 0; // we need at least 1 space character to start a new line...
- m_Lines[0].Text = "";
- for (uint i = 0; i < m_Text.size(); ++i) {
+ uint curLineWidth = 0;
+ uint curLineHeight = 0;
+ uint curLine = 0;
+ uint tempLineWidth = 0;
+ uint lastSpace = 0; // we need at least 1 space character to start a new line...
+ _lines[0].text = "";
+ for (uint i = 0; i < _text.size(); ++i) {
uint j;
- TempLineWidth = 0;
- LastSpace = 0;
- for (j = i; j < m_Text.size(); ++j) {
- if ((byte)m_Text[j] == ' ') LastSpace = j;
+ tempLineWidth = 0;
+ lastSpace = 0;
+ for (j = i; j < _text.size(); ++j) {
+ if ((byte)_text[j] == ' ')
+ lastSpace = j;
- const Common::Rect &CurCharRect = FontPtr->GetCharacterRect((byte)m_Text[j]);
- TempLineWidth += CurCharRect.width();
- TempLineWidth += FontPtr->GetGapWidth();
+ const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
+ tempLineWidth += curCharRect.width();
+ tempLineWidth += fontPtr->getGapWidth();
- if ((TempLineWidth >= m_AutoWrapThreshold) && (LastSpace > 0))
+ if ((tempLineWidth >= _autoWrapThreshold) && (lastSpace > 0))
break;
}
- if (j == m_Text.size()) LastSpace = m_Text.size(); // everything in 1 line.
+ if (j == _text.size()) // everything in 1 line.
+ lastSpace = _text.size();
- CurLineWidth = 0;
- CurLineHeight = 0;
- for (j = i; j < LastSpace; ++j) {
- m_Lines[CurLine].Text += m_Text[j];
+ curLineWidth = 0;
+ curLineHeight = 0;
+ for (j = i; j < lastSpace; ++j) {
+ _lines[curLine].text += _text[j];
- const Common::Rect &CurCharRect = FontPtr->GetCharacterRect((byte)m_Text[j]);
- CurLineWidth += CurCharRect.width();
- CurLineWidth += FontPtr->GetGapWidth();
- if ((uint) CurCharRect.height() > CurLineHeight) CurLineHeight = CurCharRect.height();
+ const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
+ curLineWidth += curCharRect.width();
+ curLineWidth += fontPtr->getGapWidth();
+ if ((uint)curCharRect.height() > curLineHeight)
+ curLineHeight = curCharRect.height();
}
- m_Lines[CurLine].BBox.right = CurLineWidth;
- m_Lines[CurLine].BBox.bottom = CurLineHeight;
- if ((uint) _width < CurLineWidth) _width = CurLineWidth;
+ _lines[curLine].bbox.right = curLineWidth;
+ _lines[curLine].bbox.bottom = curLineHeight;
+ if ((uint)_width < curLineWidth)
+ _width = curLineWidth;
- if (LastSpace < m_Text.size()) {
- ++CurLine;
- BS_ASSERT(CurLine == m_Lines.size());
- m_Lines.resize(CurLine + 1);
- m_Lines[CurLine].Text = "";
+ if (lastSpace < _text.size()) {
+ ++curLine;
+ BS_ASSERT(curLine == _lines.size());
+ _lines.resize(curLine + 1);
+ _lines[curLine].text = "";
}
- i = LastSpace;
+ i = lastSpace;
}
// Bounding-Box der einzelnen Zeilen relativ zur ersten festlegen (vor allem zentrieren).
_height = 0;
- Common::Array<LINE>::iterator Iter = m_Lines.begin();
- for (; Iter != m_Lines.end(); ++Iter) {
- Common::Rect &BBox = (*Iter).BBox;
- BBox.left = (_width - BBox.right) / 2;
- BBox.right = BBox.left + BBox.right;
- BBox.top = (Iter - m_Lines.begin()) * FontPtr->GetLineHeight();
- BBox.bottom = BBox.top + BBox.bottom;
- _height += BBox.height();
+ Common::Array<Line>::iterator iter = _lines.begin();
+ for (; iter != _lines.end(); ++iter) {
+ Common::Rect &bbox = (*iter).bbox;
+ bbox.left = (_width - bbox.right) / 2;
+ bbox.right = bbox.left + bbox.right;
+ bbox.top = (iter - _lines.begin()) * fontPtr->getLineHeight();
+ bbox.bottom = bbox.top + bbox.bottom;
+ _height += bbox.height();
}
} else {
// Keine automatische Formatierung, also wird der gesamte Text in nur eine Zeile kopiert.
- m_Lines[0].Text = m_Text;
- m_Lines[0].BBox = Common::Rect(0, 0, _width, _height);
+ _lines[0].text = _text;
+ _lines[0].bbox = Common::Rect(0, 0, _width, _height);
}
- FontPtr->release();
+ fontPtr->release();
}
-// -----------------------------------------------------------------------------
-
-void Text::UpdateMetrics(FontResource &FontResource) {
+void Text::updateMetrics(FontResource &fontResource) {
_width = 0;
_height = 0;
- for (uint i = 0; i < m_Text.size(); ++i) {
- const Common::Rect &CurRect = FontResource.GetCharacterRect((byte)m_Text[i]);
- _width += CurRect.width();
- if (i != m_Text.size() - 1) _width += FontResource.GetGapWidth();
- if (_height < CurRect.height()) _height = CurRect.height();
+ for (uint i = 0; i < _text.size(); ++i) {
+ const Common::Rect &curRect = fontResource.getCharacterRect((byte)_text[i]);
+ _width += curRect.width();
+ if (i != _text.size() - 1)
+ _width += fontResource.getGapWidth();
+ if (_height < curRect.height())
+ _height = curRect.height();
}
}
-// -----------------------------------------------------------------------------
-// Persistenz
-// -----------------------------------------------------------------------------
-
bool Text::persist(OutputPersistenceBlock &writer) {
bool result = true;
result &= RenderObject::persist(writer);
writer.write(_modulationColor);
- writer.write(m_Font);
- writer.write(m_Text);
- writer.write(m_AutoWrap);
- writer.write(m_AutoWrapThreshold);
+ writer.writeString(_font);
+ writer.writeString(_text);
+ writer.write(_autoWrap);
+ writer.write(_autoWrapThreshold);
result &= RenderObject::persistChildren(writer);
@@ -360,21 +334,21 @@ bool Text::unpersist(InputPersistenceBlock &reader) {
// Beim Laden der anderen Member werden die Set-Methoden benutzt statt der tatsächlichen Member.
// So wird das Layout automatisch aktualisiert und auch alle anderen notwendigen Methoden ausgeführt.
- Common::String Font;
- reader.read(Font);
- SetFont(Font);
+ Common::String font;
+ reader.readString(font);
+ setFont(font);
Common::String text;
- reader.read(text);
- SetText(text);
+ reader.readString(text);
+ setText(text);
- bool AutoWrap;
- reader.read(AutoWrap);
- SetAutoWrap(AutoWrap);
+ bool autoWrap;
+ reader.read(autoWrap);
+ setAutoWrap(autoWrap);
- uint AutoWrapThreshold;
- reader.read(AutoWrapThreshold);
- SetAutoWrapThreshold(AutoWrapThreshold);
+ uint autoWrapThreshold;
+ reader.read(autoWrapThreshold);
+ setAutoWrapThreshold(autoWrapThreshold);
result &= RenderObject::unpersistChildren(reader);
diff --git a/engines/sword25/gfx/text.h b/engines/sword25/gfx/text.h
index ed3a83e630..42c1cd7c5d 100644
--- a/engines/sword25/gfx/text.h
+++ b/engines/sword25/gfx/text.h
@@ -35,28 +35,16 @@
#ifndef SWORD25_TEXT_H
#define SWORD25_TEXT_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "common/rect.h"
#include "sword25/gfx/renderobject.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Forward Declarations
-// -----------------------------------------------------------------------------
-
class Kernel;
class FontResource;
class ResourceManager;
-// -----------------------------------------------------------------------------
-// Klassendefinition
-// -----------------------------------------------------------------------------
-
class Text : public RenderObject {
friend class RenderObject;
@@ -66,13 +54,13 @@ public:
@param Font der Dateiname der Fontdatei.
@return Gibt false zurück, wenn der Font nicht gefunden wurde.
*/
- bool SetFont(const Common::String &Font);
+ bool setFont(const Common::String &font);
/**
@brief Setzt den darzustellenden Text.
@param Text der darzustellende Text
*/
- void SetText(const Common::String &text);
+ void setText(const Common::String &text);
/**
@brief Setzt den Alphawert des Textes.
@@ -88,27 +76,27 @@ public:
@param AutoWrap gibt an, ob der automatische Umbruch aktiviert oder deaktiviert werden soll.
@remark Dieses Attribut wird mit dem Wert false initialisiert.
*/
- void SetAutoWrap(bool AutoWrap);
+ void setAutoWrap(bool autoWrap);
/**
@brief Legt die Längengrenze des Textes in Pixeln fest, ab der ein automatischer Zeilenumbruch vorgenommen wird.
@remark Dieses Attribut wird mit dem Wert 300 initialisiert.
@remark Eine automatische Formatierung wird nur vorgenommen, wenn diese durch einen Aufruf von SetAutoWrap() aktiviert wurde.
*/
- void SetAutoWrapThreshold(uint AutoWrapThreshold);
+ void setAutoWrapThreshold(uint autoWrapThreshold);
/**
@brief Gibt den dargestellten Text zurück.
*/
- const Common::String &GetText() {
- return m_Text;
+ const Common::String &getText() {
+ return _text;
}
/**
@brief Gibt den Namen das momentan benutzten Fonts zurück.
*/
- const Common::String &GetFont() {
- return m_Font;
+ const Common::String &getFont() {
+ return _font;
}
/**
@@ -136,44 +124,44 @@ public:
/**
@brief Gibt zurück, ob die automatische Formatierung aktiviert ist.
*/
- bool IsAutoWrapActive() const {
- return m_AutoWrap;
+ bool isAutoWrapActive() const {
+ return _autoWrap;
}
/**
@brief Gibt die Längengrenze des Textes in Pixeln zurück, ab der eine automatische Formatierung vorgenommen wird.
*/
- uint GetAutoWrapThreshold() const {
- return m_AutoWrapThreshold;
+ uint getAutoWrapThreshold() const {
+ return _autoWrapThreshold;
}
- virtual bool persist(OutputPersistenceBlock &writer);
- virtual bool unpersist(InputPersistenceBlock &reader);
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
protected:
virtual bool doRender();
private:
- Text(RenderObjectPtr<RenderObject> ParentPtr);
- Text(InputPersistenceBlock &Reader, RenderObjectPtr<RenderObject> ParentPtr, uint Handle);
-
- uint _modulationColor;
- Common::String m_Font;
- Common::String m_Text;
- bool m_AutoWrap;
- uint m_AutoWrapThreshold;
-
- struct LINE {
- Common::Rect BBox;
- Common::String Text;
+ Text(RenderObjectPtr<RenderObject> parentPtr);
+ Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+ uint _modulationColor;
+ Common::String _font;
+ Common::String _text;
+ bool _autoWrap;
+ uint _autoWrapThreshold;
+
+ struct Line {
+ Common::Rect bbox;
+ Common::String text;
};
- Common::Array<LINE> m_Lines;
+ Common::Array<Line> _lines;
- void UpdateFormat();
- void UpdateMetrics(FontResource &FontResource);
- ResourceManager *GetResourceManager();
- FontResource *LockFontResource();
+ void updateFormat();
+ void updateMetrics(FontResource &fontResource);
+ ResourceManager *getResourceManager();
+ FontResource *lockFontResource();
};
} // End of namespace Sword25
diff --git a/engines/sword25/gfx/timedrenderobject.h b/engines/sword25/gfx/timedrenderobject.h
index 94d882d18e..6fee19882a 100644
--- a/engines/sword25/gfx/timedrenderobject.h
+++ b/engines/sword25/gfx/timedrenderobject.h
@@ -32,14 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// System Includes
-// -----------------------------------------------------------------------------
-
-// -----------------------------------------------------------------------------
-// Engine Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/gfx/renderobject.h"
diff --git a/engines/sword25/input/inputengine.cpp b/engines/sword25/input/inputengine.cpp
index a57af23e6b..82d5cad588 100644
--- a/engines/sword25/input/inputengine.cpp
+++ b/engines/sword25/input/inputengine.cpp
@@ -39,7 +39,6 @@
#include "common/system.h"
#include "common/util.h"
#include "sword25/kernel/kernel.h"
-#include "sword25/kernel/callbackregistry.h"
#include "sword25/kernel/inputpersistenceblock.h"
#include "sword25/kernel/outputpersistenceblock.h"
#include "sword25/input/inputengine.h"
@@ -51,24 +50,24 @@ namespace Sword25 {
InputEngine::InputEngine(Kernel *pKernel) :
Service(pKernel),
- m_CurrentState(0),
- m_LeftMouseDown(false),
- m_RightMouseDown(false),
- m_MouseX(0),
- m_MouseY(0),
- m_LeftDoubleClick(false),
- m_DoubleClickTime(DOUBLE_CLICK_TIME),
- m_DoubleClickRectWidth(DOUBLE_CLICK_RECT_SIZE),
- m_DoubleClickRectHeight(DOUBLE_CLICK_RECT_SIZE),
- m_LastLeftClickTime(0),
- m_LastLeftClickMouseX(0),
- m_LastLeftClickMouseY(0) {
- memset(m_KeyboardState[0], 0, sizeof(m_KeyboardState[0]));
- memset(m_KeyboardState[1], 0, sizeof(m_KeyboardState[1]));
- m_LeftMouseState[0] = false;
- m_LeftMouseState[1] = false;
- m_RightMouseState[0] = false;
- m_RightMouseState[1] = false;
+ _currentState(0),
+ _leftMouseDown(false),
+ _rightMouseDown(false),
+ _mouseX(0),
+ _mouseY(0),
+ _leftDoubleClick(false),
+ _doubleClickTime(DOUBLE_CLICK_TIME),
+ _doubleClickRectWidth(DOUBLE_CLICK_RECT_SIZE),
+ _doubleClickRectHeight(DOUBLE_CLICK_RECT_SIZE),
+ _lastLeftClickTime(0),
+ _lastLeftClickMouseX(0),
+ _lastLeftClickMouseY(0) {
+ memset(_keyboardState[0], 0, sizeof(_keyboardState[0]));
+ memset(_keyboardState[1], 0, sizeof(_keyboardState[1]));
+ _leftMouseState[0] = false;
+ _leftMouseState[1] = false;
+ _rightMouseState[0] = false;
+ _rightMouseState[1] = false;
if (!registerScriptBindings())
BS_LOG_ERRORLN("Script bindings could not be registered.");
@@ -76,22 +75,25 @@ InputEngine::InputEngine(Kernel *pKernel) :
BS_LOGLN("Script bindings registered.");
}
-Service *InputEngine_CreateObject(Kernel *pKernel) {
- return new InputEngine(pKernel);
+InputEngine::~InputEngine() {
+ unregisterScriptBindings();
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::Init() {
+bool InputEngine::init() {
// No initialisation needed
return true;
}
-// -----------------------------------------------------------------------------
-
-void InputEngine::Update() {
+void InputEngine::update() {
Common::Event event;
- m_CurrentState ^= 1;
+
+ // We keep two sets of keyboard states: The current one, and that of
+ // the previous frame. This allows us to detect which keys changed
+ // state. Also, by keeping a single central keystate array, we
+ // ensure that all script queries for key state during a single
+ // frame get the same consistent replies.
+ _currentState ^= 1;
+ memcpy(_keyboardState[_currentState], _keyboardState[_currentState ^ 1], sizeof(_keyboardState[0]));
// Loop through processing any pending events
bool handleEvents = true;
@@ -99,31 +101,33 @@ void InputEngine::Update() {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
- m_LeftMouseDown = event.type == Common::EVENT_LBUTTONDOWN;
- m_MouseX = event.mouse.x;
- m_MouseY = event.mouse.y;
+ _leftMouseDown = event.type == Common::EVENT_LBUTTONDOWN;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
handleEvents = false;
break;
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_RBUTTONUP:
- m_RightMouseDown = event.type == Common::EVENT_RBUTTONDOWN;
- m_MouseX = event.mouse.x;
- m_MouseY = event.mouse.y;
+ _rightMouseDown = event.type == Common::EVENT_RBUTTONDOWN;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
handleEvents = false;
break;
case Common::EVENT_MOUSEMOVE:
- m_MouseX = event.mouse.x;
- m_MouseY = event.mouse.y;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
break;
case Common::EVENT_KEYDOWN:
case Common::EVENT_KEYUP:
- AlterKeyboardState(event.kbd.keycode, (event.type == Common::EVENT_KEYDOWN) ? 0x80 : 0);
- break;
+ // FIXME - Need to work out how to expose getDebugger() to this module
+ //if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d && event.type == Common::EVENT_KEYDOWN) {
+ // _vm->getDebugger()->attach();
+ // _vm->getDebugger()->onFrame();
+ //}
- case Common::EVENT_QUIT:
- Kernel::GetInstance()->GetWindow()->SetWindowAlive(false);
+ alterKeyboardState(event.kbd.keycode, (event.type == Common::EVENT_KEYDOWN) ? 0x80 : 0);
break;
default:
@@ -131,267 +135,153 @@ void InputEngine::Update() {
}
}
- m_LeftMouseState[m_CurrentState] = m_LeftMouseDown;
- m_RightMouseState[m_CurrentState] = m_RightMouseDown;
+ _leftMouseState[_currentState] = _leftMouseDown;
+ _rightMouseState[_currentState] = _rightMouseDown;
- TestForLeftDoubleClick();
+ testForLeftDoubleClick();
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::IsLeftMouseDown() {
- return m_LeftMouseDown;
+bool InputEngine::isLeftMouseDown() {
+ return _leftMouseDown;
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::IsRightMouseDown() {
- return m_RightMouseDown;
+bool InputEngine::isRightMouseDown() {
+ return _rightMouseDown;
}
-// -----------------------------------------------------------------------------
-
-void InputEngine::TestForLeftDoubleClick() {
- m_LeftDoubleClick = false;
+void InputEngine::testForLeftDoubleClick() {
+ _leftDoubleClick = false;
// Only bother checking for a double click if the left mouse button was clicked
- if (WasLeftMouseDown()) {
+ if (wasLeftMouseDown()) {
// Get the time now
- uint Now = Kernel::GetInstance()->GetMilliTicks();
+ uint now = Kernel::getInstance()->getMilliTicks();
// A double click is signalled if
// 1. The two clicks are close enough together
// 2. The mouse cursor hasn't moved much
- if (Now - m_LastLeftClickTime <= m_DoubleClickTime &&
- ABS(m_MouseX - m_LastLeftClickMouseX) <= m_DoubleClickRectWidth / 2 &&
- ABS(m_MouseY - m_LastLeftClickMouseY) <= m_DoubleClickRectHeight / 2) {
- m_LeftDoubleClick = true;
+ if (now - _lastLeftClickTime <= _doubleClickTime &&
+ ABS(_mouseX - _lastLeftClickMouseX) <= _doubleClickRectWidth / 2 &&
+ ABS(_mouseY - _lastLeftClickMouseY) <= _doubleClickRectHeight / 2) {
+ _leftDoubleClick = true;
// Reset the time and position of the last click, so that clicking is not
// interpreted as the first click of a further double-click
- m_LastLeftClickTime = 0;
- m_LastLeftClickMouseX = 0;
- m_LastLeftClickMouseY = 0;
+ _lastLeftClickTime = 0;
+ _lastLeftClickMouseX = 0;
+ _lastLeftClickMouseY = 0;
} else {
// There is no double click. Remember the position and time of the click,
// in case it's the first click of a double-click sequence
- m_LastLeftClickTime = Now;
- m_LastLeftClickMouseX = m_MouseX;
- m_LastLeftClickMouseY = m_MouseY;
+ _lastLeftClickTime = now;
+ _lastLeftClickMouseX = _mouseX;
+ _lastLeftClickMouseY = _mouseY;
}
}
}
-// -----------------------------------------------------------------------------
-
-void InputEngine::AlterKeyboardState(int keycode, byte newState) {
- m_KeyboardState[m_CurrentState][keycode] = newState;
+void InputEngine::alterKeyboardState(int keycode, byte newState) {
+ assert(keycode < ARRAYSIZE(_keyboardState[_currentState]));
+ _keyboardState[_currentState][keycode] = newState;
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::IsLeftDoubleClick() {
- return m_LeftDoubleClick;
+bool InputEngine::isLeftDoubleClick() {
+ return _leftDoubleClick;
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::WasLeftMouseDown() {
- return (m_LeftMouseState[m_CurrentState] == false) && (m_LeftMouseState[m_CurrentState ^ 1] == true);
+bool InputEngine::wasLeftMouseDown() {
+ return (_leftMouseState[_currentState] == false) && (_leftMouseState[_currentState ^ 1] == true);
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::WasRightMouseDown() {
- return (m_RightMouseState[m_CurrentState] == false) && (m_RightMouseState[m_CurrentState ^ 1] == true);
+bool InputEngine::wasRightMouseDown() {
+ return (_rightMouseState[_currentState] == false) && (_rightMouseState[_currentState ^ 1] == true);
}
-// -----------------------------------------------------------------------------
-
-int InputEngine::GetMouseX() {
- return m_MouseX;
+int InputEngine::getMouseX() {
+ return _mouseX;
}
-// -----------------------------------------------------------------------------
-
-int InputEngine::GetMouseY() {
- return m_MouseY;
+int InputEngine::getMouseY() {
+ return _mouseY;
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::IsKeyDown(uint KeyCode) {
- return (m_KeyboardState[m_CurrentState][KeyCode] & 0x80) != 0;
-}
-
-// -----------------------------------------------------------------------------
-
-bool InputEngine::WasKeyDown(uint KeyCode) {
- return ((m_KeyboardState[m_CurrentState][KeyCode] & 0x80) == 0) &&
- ((m_KeyboardState[m_CurrentState ^ 1][KeyCode] & 0x80) != 0);
-}
-
-// -----------------------------------------------------------------------------
-
-void InputEngine::SetMouseX(int PosX) {
- m_MouseX = PosX;
- g_system->warpMouse(m_MouseX, m_MouseY);
+bool InputEngine::isKeyDown(uint keyCode) {
+ assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
+ return (_keyboardState[_currentState][keyCode] & 0x80) != 0;
}
-// -----------------------------------------------------------------------------
-
-void InputEngine::SetMouseY(int PosY) {
- m_MouseY = PosY;
- g_system->warpMouse(m_MouseX, m_MouseY);
+bool InputEngine::wasKeyDown(uint keyCode) {
+ assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
+ return ((_keyboardState[_currentState][keyCode] & 0x80) == 0) &&
+ ((_keyboardState[_currentState ^ 1][keyCode] & 0x80) != 0);
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::RegisterCharacterCallback(CharacterCallback Callback) {
- if (Common::find(m_CharacterCallbacks.begin(), m_CharacterCallbacks.end(), Callback) == m_CharacterCallbacks.end()) {
- m_CharacterCallbacks.push_back(Callback);
- return true;
- } else {
- BS_LOG_WARNINGLN("Tried to register an CharacterCallback that was already registered.");
- return false;
- }
+void InputEngine::setMouseX(int posX) {
+ _mouseX = posX;
+ g_system->warpMouse(_mouseX, _mouseY);
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::UnregisterCharacterCallback(CharacterCallback Callback) {
- Common::List<CharacterCallback>::iterator CallbackIter = Common::find(m_CharacterCallbacks.begin(),
- m_CharacterCallbacks.end(), Callback);
- if (CallbackIter != m_CharacterCallbacks.end()) {
- m_CharacterCallbacks.erase(CallbackIter);
- return true;
- } else {
- BS_LOG_WARNINGLN("Tried to unregister an CharacterCallback that was not previously registered.");
- return false;
- }
+void InputEngine::setMouseY(int posY) {
+ _mouseY = posY;
+ g_system->warpMouse(_mouseX, _mouseY);
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::RegisterCommandCallback(CommandCallback Callback) {
- if (Common::find(m_CommandCallbacks.begin(), m_CommandCallbacks.end(), Callback) == m_CommandCallbacks.end()) {
- m_CommandCallbacks.push_back(Callback);
- return true;
- } else {
- BS_LOG_WARNINGLN("Tried to register an CommandCallback that was already registered.");
- return false;
- }
+void InputEngine::setCharacterCallback(CharacterCallback callback) {
+ _characterCallback = callback;
}
-// -----------------------------------------------------------------------------
-
-bool InputEngine::UnregisterCommandCallback(CommandCallback Callback) {
- Common::List<CommandCallback>::iterator CallbackIter =
- Common::find(m_CommandCallbacks.begin(), m_CommandCallbacks.end(), Callback);
- if (CallbackIter != m_CommandCallbacks.end()) {
- m_CommandCallbacks.erase(CallbackIter);
- return true;
- } else {
- BS_LOG_WARNINGLN("Tried to unregister an CommandCallback that was not previously registered.");
- return false;
- }
+void InputEngine::setCommandCallback(CommandCallback callback) {
+ _commandCallback = callback;
}
-// -----------------------------------------------------------------------------
-
-void InputEngine::ReportCharacter(byte Character) {
- Common::List<CharacterCallback>::const_iterator CallbackIter = m_CharacterCallbacks.begin();
- while (CallbackIter != m_CharacterCallbacks.end()) {
- // Iterator vor dem Aufruf erhöhen und im Folgendem auf einer Kopie arbeiten.
- // Dieses Vorgehen ist notwendig da der Iterator möglicherweise von der Callbackfunktion durch das Deregistrieren des Callbacks
- // invalidiert wird.
- Common::List<CharacterCallback>::const_iterator CurCallbackIter = CallbackIter;
- ++CallbackIter;
-
- (*CurCallbackIter)(Character);
- }
+void InputEngine::reportCharacter(byte character) {
+ if (_characterCallback)
+ (*_characterCallback)(character);
}
-// -----------------------------------------------------------------------------
-
-void InputEngine::ReportCommand(KEY_COMMANDS Command) {
- Common::List<CommandCallback>::const_iterator CallbackIter = m_CommandCallbacks.begin();
- while (CallbackIter != m_CommandCallbacks.end()) {
- // Iterator vor dem Aufruf erhöhen und im Folgendem auf einer Kopie arbeiten.
- // Dieses Vorgehen ist notwendig da der Iterator möglicherweise von der Callbackfunktion durch das Deregistrieren des Callbacks
- // invalidiert wird.
- Common::List<CommandCallback>::const_iterator CurCallbackIter = CallbackIter;
- ++CallbackIter;
-
- (*CurCallbackIter)(Command);
- }
+void InputEngine::reportCommand(KEY_COMMANDS command) {
+ if (_commandCallback)
+ (*_commandCallback)(command);
}
-// -----------------------------------------------------------------------------
-// Persistenz
-// -----------------------------------------------------------------------------
-
bool InputEngine::persist(OutputPersistenceBlock &writer) {
- // Anzahl an Command-Callbacks persistieren.
- writer.write(m_CommandCallbacks.size());
-
- // Alle Command-Callbacks einzeln persistieren.
- {
- Common::List<CommandCallback>::const_iterator It = m_CommandCallbacks.begin();
- while (It != m_CommandCallbacks.end()) {
- writer.write(CallbackRegistry::getInstance().resolveCallbackPointer(*It));
- ++It;
- }
- }
-
- // Anzahl an Character-Callbacks persistieren.
- writer.write(m_CharacterCallbacks.size());
-
- // Alle Character-Callbacks einzeln persistieren.
- {
- Common::List<CharacterCallback>::const_iterator It = m_CharacterCallbacks.begin();
- while (It != m_CharacterCallbacks.end()) {
- writer.write(CallbackRegistry::getInstance().resolveCallbackPointer(*It));
- ++It;
- }
- }
+ // Write out the number of command callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaCommandCB");
+
+ // Write out the number of command callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaCharacterCB");
return true;
}
-// -----------------------------------------------------------------------------
-
bool InputEngine::unpersist(InputPersistenceBlock &reader) {
- // Command-Callbackliste leeren.
- m_CommandCallbacks.clear();
-
- // Anzahl an Command-Callbacks lesen.
- uint CommandCallbackCount;
- reader.read(CommandCallbackCount);
-
- // Alle Command-Callbacks wieder herstellen.
- for (uint i = 0; i < CommandCallbackCount; ++i) {
- Common::String CallbackFunctionName;
- reader.read(CallbackFunctionName);
-
- m_CommandCallbacks.push_back(reinterpret_cast<CommandCallback>(
- CallbackRegistry::getInstance().resolveCallbackFunction(CallbackFunctionName)));
- }
-
- // Character-Callbackliste leeren.
- m_CharacterCallbacks.clear();
-
- // Anzahl an Character-Callbacks lesen.
- uint CharacterCallbackCount;
- reader.read(CharacterCallbackCount);
-
- // Alle Character-Callbacks wieder herstellen.
- for (uint i = 0; i < CharacterCallbackCount; ++i) {
- Common::String CallbackFunctionName;
- reader.read(CallbackFunctionName);
-
- m_CharacterCallbacks.push_back(reinterpret_cast<CharacterCallback>(CallbackRegistry::getInstance().resolveCallbackFunction(CallbackFunctionName)));
- }
+ Common::String callbackFunctionName;
+
+ // Read number of command callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ uint commandCallbackCount;
+ reader.read(commandCallbackCount);
+ assert(commandCallbackCount == 1);
+
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaCommandCB");
+
+ // Read number of character callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ uint characterCallbackCount;
+ reader.read(characterCallbackCount);
+ assert(characterCallbackCount == 1);
+
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaCharacterCB");
return reader.isGood();
}
diff --git a/engines/sword25/input/inputengine.h b/engines/sword25/input/inputengine.h
index 540817b5ce..946d6a8e5e 100644
--- a/engines/sword25/input/inputengine.h
+++ b/engines/sword25/input/inputengine.h
@@ -45,11 +45,11 @@
#ifndef SWORD25_INPUTENGINE_H
#define SWORD25_INPUTENGINE_H
-/// Includes
+#include "common/keyboard.h"
+
#include "sword25/kernel/common.h"
#include "sword25/kernel/service.h"
#include "sword25/kernel/persistable.h"
-#include "sword25/kernel/callbackregistry.h"
namespace Sword25 {
@@ -58,104 +58,106 @@ namespace Sword25 {
class InputEngine : public Service, public Persistable {
public:
InputEngine(Kernel *pKernel);
- ~InputEngine() {};
+ ~InputEngine();
// NOTE: These codes are registered in inputengine_script.cpp
- // Any changes to these enums must also adjust the above file.
+ // If you add or remove entries of this enum, you must also adjust
+ // the above file.
enum KEY_CODES {
- KEY_BACKSPACE = 0x08,
- KEY_TAB = 0x09,
- KEY_CLEAR = 0x0C,
- KEY_RETURN = 0x0D,
- KEY_PAUSE = 0x13,
- KEY_CAPSLOCK = 0x14,
- KEY_ESCAPE = 0x1B,
- KEY_SPACE = 0x20,
- KEY_PAGEUP = 0x21,
- KEY_PAGEDOWN = 0x22,
- KEY_END = 0x23,
- KEY_HOME = 0x24,
- KEY_LEFT = 0x25,
- KEY_UP = 0x26,
- KEY_RIGHT = 0x27,
- KEY_DOWN = 0x28,
- KEY_PRINTSCREEN = 0x2C,
- KEY_INSERT = 0x2D,
- KEY_DELETE = 0x2E,
- KEY_0 = 0x30,
- KEY_1 = 0x31,
- KEY_2 = 0x32,
- KEY_3 = 0x33,
- KEY_4 = 0x34,
- KEY_5 = 0x35,
- KEY_6 = 0x36,
- KEY_7 = 0x37,
- KEY_8 = 0x38,
- KEY_9 = 0x39,
- KEY_A = 0x41,
- KEY_B = 0x42,
- KEY_C = 0x43,
- KEY_D = 0x44,
- KEY_E = 0x45,
- KEY_F = 0x46,
- KEY_G = 0x47,
- KEY_H = 0x48,
- KEY_I = 0x49,
- KEY_J = 0x4A,
- KEY_K = 0x4B,
- KEY_L = 0x4C,
- KEY_M = 0x4D,
- KEY_N = 0x4E,
- KEY_O = 0x4F,
- KEY_P = 0x50,
- KEY_Q = 0x51,
- KEY_R = 0x52,
- KEY_S = 0x53,
- KEY_T = 0x54,
- KEY_U = 0x55,
- KEY_V = 0x56,
- KEY_W = 0x57,
- KEY_X = 0x58,
- KEY_Y = 0x59,
- KEY_Z = 0x5A,
- KEY_NUMPAD0 = 0x60,
- KEY_NUMPAD1 = 0x61,
- KEY_NUMPAD2 = 0x62,
- KEY_NUMPAD3 = 0x63,
- KEY_NUMPAD4 = 0x64,
- KEY_NUMPAD5 = 0x65,
- KEY_NUMPAD6 = 0x66,
- KEY_NUMPAD7 = 0x67,
- KEY_NUMPAD8 = 0x68,
- KEY_NUMPAD9 = 0x69,
- KEY_MULTIPLY = 0x6A,
- KEY_ADD = 0x6B,
- KEY_SEPARATOR = 0x6C,
- KEY_SUBTRACT = 0x6D,
- KEY_DECIMAL = 0x6E,
- KEY_DIVIDE = 0x6F,
- KEY_F1 = 0x70,
- KEY_F2 = 0x71,
- KEY_F3 = 0x72,
- KEY_F4 = 0x73,
- KEY_F5 = 0x74,
- KEY_F6 = 0x75,
- KEY_F7 = 0x76,
- KEY_F8 = 0x77,
- KEY_F9 = 0x78,
- KEY_F10 = 0x79,
- KEY_F11 = 0x7A,
- KEY_F12 = 0x7B,
- KEY_NUMLOCK = 0x90,
- KEY_SCROLL = 0x91,
- KEY_LSHIFT = 0xA0,
- KEY_RSHIFT = 0xA1,
- KEY_LCONTROL = 0xA2,
- KEY_RCONTROL = 0xA3
+ KEY_BACKSPACE = Common::KEYCODE_BACKSPACE,
+ KEY_TAB = Common::KEYCODE_TAB,
+ KEY_CLEAR = Common::KEYCODE_CLEAR,
+ KEY_RETURN = Common::KEYCODE_RETURN,
+ KEY_PAUSE = Common::KEYCODE_PAUSE,
+ KEY_CAPSLOCK = Common::KEYCODE_CAPSLOCK,
+ KEY_ESCAPE = Common::KEYCODE_ESCAPE,
+ KEY_SPACE = Common::KEYCODE_SPACE,
+ KEY_PAGEUP = Common::KEYCODE_PAGEUP,
+ KEY_PAGEDOWN = Common::KEYCODE_PAGEDOWN,
+ KEY_END = Common::KEYCODE_END,
+ KEY_HOME = Common::KEYCODE_HOME,
+ KEY_LEFT = Common::KEYCODE_LEFT,
+ KEY_UP = Common::KEYCODE_UP,
+ KEY_RIGHT = Common::KEYCODE_RIGHT,
+ KEY_DOWN = Common::KEYCODE_DOWN,
+ KEY_PRINTSCREEN = Common::KEYCODE_PRINT,
+ KEY_INSERT = Common::KEYCODE_INSERT,
+ KEY_DELETE = Common::KEYCODE_DELETE,
+ KEY_0 = Common::KEYCODE_0,
+ KEY_1 = Common::KEYCODE_1,
+ KEY_2 = Common::KEYCODE_2,
+ KEY_3 = Common::KEYCODE_3,
+ KEY_4 = Common::KEYCODE_4,
+ KEY_5 = Common::KEYCODE_5,
+ KEY_6 = Common::KEYCODE_6,
+ KEY_7 = Common::KEYCODE_7,
+ KEY_8 = Common::KEYCODE_8,
+ KEY_9 = Common::KEYCODE_9,
+ KEY_A = Common::KEYCODE_a,
+ KEY_B = Common::KEYCODE_b,
+ KEY_C = Common::KEYCODE_c,
+ KEY_D = Common::KEYCODE_d,
+ KEY_E = Common::KEYCODE_e,
+ KEY_F = Common::KEYCODE_f,
+ KEY_G = Common::KEYCODE_g,
+ KEY_H = Common::KEYCODE_h,
+ KEY_I = Common::KEYCODE_i,
+ KEY_J = Common::KEYCODE_j,
+ KEY_K = Common::KEYCODE_k,
+ KEY_L = Common::KEYCODE_l,
+ KEY_M = Common::KEYCODE_m,
+ KEY_N = Common::KEYCODE_n,
+ KEY_O = Common::KEYCODE_o,
+ KEY_P = Common::KEYCODE_p,
+ KEY_Q = Common::KEYCODE_q,
+ KEY_R = Common::KEYCODE_r,
+ KEY_S = Common::KEYCODE_s,
+ KEY_T = Common::KEYCODE_t,
+ KEY_U = Common::KEYCODE_u,
+ KEY_V = Common::KEYCODE_v,
+ KEY_W = Common::KEYCODE_w,
+ KEY_X = Common::KEYCODE_x,
+ KEY_Y = Common::KEYCODE_y,
+ KEY_Z = Common::KEYCODE_z,
+ KEY_NUMPAD0 = Common::KEYCODE_KP0,
+ KEY_NUMPAD1 = Common::KEYCODE_KP1,
+ KEY_NUMPAD2 = Common::KEYCODE_KP2,
+ KEY_NUMPAD3 = Common::KEYCODE_KP3,
+ KEY_NUMPAD4 = Common::KEYCODE_KP4,
+ KEY_NUMPAD5 = Common::KEYCODE_KP5,
+ KEY_NUMPAD6 = Common::KEYCODE_KP6,
+ KEY_NUMPAD7 = Common::KEYCODE_KP7,
+ KEY_NUMPAD8 = Common::KEYCODE_KP8,
+ KEY_NUMPAD9 = Common::KEYCODE_KP9,
+ KEY_MULTIPLY = Common::KEYCODE_KP_MULTIPLY,
+ KEY_ADD = Common::KEYCODE_KP_PLUS,
+ KEY_SEPARATOR = Common::KEYCODE_EQUALS, // FIXME: This mapping is just a wild guess!!
+ KEY_SUBTRACT = Common::KEYCODE_KP_MINUS,
+ KEY_DECIMAL = Common::KEYCODE_KP_PERIOD,
+ KEY_DIVIDE = Common::KEYCODE_KP_DIVIDE,
+ KEY_F1 = Common::KEYCODE_F1,
+ KEY_F2 = Common::KEYCODE_F2,
+ KEY_F3 = Common::KEYCODE_F3,
+ KEY_F4 = Common::KEYCODE_F4,
+ KEY_F5 = Common::KEYCODE_F5,
+ KEY_F6 = Common::KEYCODE_F6,
+ KEY_F7 = Common::KEYCODE_F7,
+ KEY_F8 = Common::KEYCODE_F8,
+ KEY_F9 = Common::KEYCODE_F9,
+ KEY_F10 = Common::KEYCODE_F10,
+ KEY_F11 = Common::KEYCODE_F11,
+ KEY_F12 = Common::KEYCODE_F12,
+ KEY_NUMLOCK = Common::KEYCODE_NUMLOCK,
+ KEY_SCROLL = Common::KEYCODE_SCROLLOCK,
+ KEY_LSHIFT = Common::KEYCODE_LSHIFT,
+ KEY_RSHIFT = Common::KEYCODE_RSHIFT,
+ KEY_LCONTROL = Common::KEYCODE_LCTRL,
+ KEY_RCONTROL = Common::KEYCODE_RCTRL
};
- // NOTE: These codes are registered in inputengine_script.cpp.
- // Any changes to these enums must also adjust the above file.
+ // NOTE: These codes are registered in inputengine_script.cpp
+ // If you add or remove entries of this enum, you must also adjust
+ // the above file.
enum KEY_COMMANDS {
KEY_COMMAND_ENTER = 1,
KEY_COMMAND_LEFT = 2,
@@ -176,7 +178,7 @@ public:
* Initialises the input engine
* @return Returns a true on success, otherwise false.
*/
- bool Init();
+ bool init();
/**
* Performs a "tick" of the input engine.
@@ -185,17 +187,17 @@ public:
* of the input engine that are not running in their own thread, or to perform
* additional administrative tasks that are needed.
*/
- void Update();
+ void update();
/**
* Returns true if the left mouse button is pressed
*/
- bool IsLeftMouseDown();
+ bool isLeftMouseDown();
/**
* Returns true if the right mouse button is pressed.
*/
- bool IsRightMouseDown();
+ bool isRightMouseDown();
/**
* Returns true if the left mouse button was pressed and released.
@@ -203,7 +205,7 @@ public:
* The difference between this and IsLeftMouseDown() is that this only returns
* true when the left mouse button is released.
*/
- bool WasLeftMouseDown();
+ bool wasLeftMouseDown();
/**
* Returns true if the right mouse button was pressed and released.
@@ -211,39 +213,39 @@ public:
* The difference between this and IsRightMouseDown() is that this only returns
* true when the right mouse button is released.
*/
- bool WasRightMouseDown();
+ bool wasRightMouseDown();
/**
* Returns true if the left mouse button double click was done
*/
- bool IsLeftDoubleClick();
+ bool isLeftDoubleClick();
/**
* Returns the X position of the cursor in pixels
*/
- int GetMouseX();
+ int getMouseX();
/**
* Returns the Y position of the cursor in pixels
*/
- int GetMouseY();
+ int getMouseY();
/**
* Sets the X position of the cursor in pixels
*/
- void SetMouseX(int PosX);
+ void setMouseX(int posX);
/**
* Sets the Y position of the cursor in pixels
*/
- void SetMouseY(int PosY);
+ void setMouseY(int posY);
/**
* Returns true if a given key was pressed
* @param KeyCode The key code to be checked
* @return Returns true if the given key is done, otherwise false.
*/
- bool IsKeyDown(uint KeyCode);
+ bool isKeyDown(uint keyCode);
/**
* Returns true if a certain key was pushed and released.
@@ -253,79 +255,66 @@ public:
* strings that users type.
* @param KeyCode The key code to be checked
*/
- bool WasKeyDown(uint KeyCode);
+ bool wasKeyDown(uint keyCode);
- typedef CallbackPtr CharacterCallback;
+ typedef void (*CharacterCallback)(int command);
/**
* Registers a callback function for keyboard input.
*
- * The callbacks that are registered with this function will be called whenever an
+ * The callback that is registered with this function will be called whenever an
* input key is pressed. A letter entry is different from the query using the
- * methods IsKeyDown () and WasKeyDown () in the sense that are treated instead
+ * methods isKeyDown() and wasKeyDown() in the sense that are treated instead
* of actual scan-coded letters. These were taken into account, among other things:
* the keyboard layout, the condition the Shift and Caps Lock keys and the repetition
* of longer holding the key.
* The input of strings by the user through use of callbacks should be implemented.
- * @return Returns true if the function was registered, otherwise false.
*/
- bool RegisterCharacterCallback(CallbackPtr Callback);
-
- /**
- * De-registeres a previously registered callback function.
- * @return Returns true if the function could be de-registered, otherwise false.
- */
- bool UnregisterCharacterCallback(CallbackPtr Callback);
+ void setCharacterCallback(CharacterCallback callback);
- typedef CallbackPtr CommandCallback;
+ typedef void (*CommandCallback)(int command);
/**
* Registers a callback function for the input of commands that can have influence on the string input
*
- * The callbacks that are registered with this function will be called whenever the input service
+ * The callback that is registered with this function will be called whenever the input service
* has a key that affects the character string input. This could be the following keys:
* Enter, End, Left, Right, ...
* The input of strings by the user through the use of callbacks should be implemented.
- * @return Returns true if the function was registered, otherwise false.
- */
- bool RegisterCommandCallback(CallbackPtr Callback);
-
- /**
- * Un-register a callback function for the input of commands that can have an influence on the string input.
- * @return Returns true if the function could be de-registered, otherwise false.
*/
- bool UnregisterCommandCallback(CommandCallback Callback);
+ void setCommandCallback(CommandCallback callback);
- void ReportCharacter(byte Character);
- void ReportCommand(KEY_COMMANDS Command);
+ void reportCharacter(byte character);
+ void reportCommand(KEY_COMMANDS command);
bool persist(OutputPersistenceBlock &writer);
bool unpersist(InputPersistenceBlock &reader);
private:
bool registerScriptBindings();
+ void unregisterScriptBindings();
private:
- void TestForLeftDoubleClick();
- void AlterKeyboardState(int keycode, byte newState);
-
- byte m_KeyboardState[2][256];
- bool m_LeftMouseState[2];
- bool m_RightMouseState[2];
- uint m_CurrentState;
- int m_MouseX;
- int m_MouseY;
- bool m_LeftMouseDown;
- bool m_RightMouseDown;
- bool m_LeftDoubleClick;
- uint m_DoubleClickTime;
- int m_DoubleClickRectWidth;
- int m_DoubleClickRectHeight;
- uint m_LastLeftClickTime;
- int m_LastLeftClickMouseX;
- int m_LastLeftClickMouseY;
- Common::List<CommandCallback> m_CommandCallbacks;
- Common::List<CharacterCallback> m_CharacterCallbacks;
+ void testForLeftDoubleClick();
+ void alterKeyboardState(int keycode, byte newState);
+
+ byte _keyboardState[2][512];
+ bool _leftMouseState[2];
+ bool _rightMouseState[2];
+ uint _currentState;
+ int _mouseX;
+ int _mouseY;
+ bool _leftMouseDown;
+ bool _rightMouseDown;
+ bool _leftDoubleClick;
+ uint _doubleClickTime;
+ int _doubleClickRectWidth;
+ int _doubleClickRectHeight;
+ uint _lastLeftClickTime;
+ int _lastLeftClickMouseX;
+ int _lastLeftClickMouseY;
+ CommandCallback _commandCallback;
+ CharacterCallback _characterCallback;
};
} // End of namespace Sword25
diff --git a/engines/sword25/input/inputengine_script.cpp b/engines/sword25/input/inputengine_script.cpp
index 38ecc3cf56..2f16a21377 100644
--- a/engines/sword25/input/inputengine_script.cpp
+++ b/engines/sword25/input/inputengine_script.cpp
@@ -32,15 +32,10 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "common/ptr.h"
#include "common/str.h"
#include "sword25/kernel/common.h"
#include "sword25/kernel/kernel.h"
-#include "sword25/kernel/callbackregistry.h"
#include "sword25/script/script.h"
#include "sword25/script/luabindhelper.h"
#include "sword25/script/luacallback.h"
@@ -51,270 +46,203 @@
namespace Sword25 {
-using namespace Lua;
-
-// -----------------------------------------------------------------------------
-// Callback-Objekte
-// -----------------------------------------------------------------------------
-
-static void TheCharacterCallback(int Character);
-static void TheCommandCallback(int Command);
+static void theCharacterCallback(int character);
+static void theCommandCallback(int command);
namespace {
class CharacterCallbackClass : public LuaCallback {
public:
- CharacterCallbackClass(lua_State *L) : LuaCallback(L) {};
+ CharacterCallbackClass(lua_State *L) : LuaCallback(L) {}
- Common::String Character;
+ Common::String _character;
protected:
int PreFunctionInvokation(lua_State *L) {
- lua_pushstring(L, Character.c_str());
+ lua_pushstring(L, _character.c_str());
return 1;
}
};
-Common::SharedPtr<CharacterCallbackClass> CharacterCallbackPtr;
-// -----------------------------------------------------------------------------
+static CharacterCallbackClass *characterCallbackPtr = 0; // FIXME: should be turned into InputEngine member var
class CommandCallbackClass : public LuaCallback {
public:
CommandCallbackClass(lua_State *L) : LuaCallback(L) {
- Command = InputEngine::KEY_COMMAND_BACKSPACE;
+ _command = InputEngine::KEY_COMMAND_BACKSPACE;
}
- InputEngine::KEY_COMMANDS Command;
+ InputEngine::KEY_COMMANDS _command;
protected:
- int PreFunctionInvokation(lua_State *L) {
- lua_pushnumber(L, Command);
+ int preFunctionInvokation(lua_State *L) {
+ lua_pushnumber(L, _command);
return 1;
}
};
-Common::SharedPtr<CommandCallbackClass> CommandCallbackPtr;
-// -------------------------------------------------------------------------
+static CommandCallbackClass *commandCallbackPtr = 0; // FIXME: should be turned into InputEngine member var
-struct CallbackfunctionRegisterer {
- CallbackfunctionRegisterer() {
- CallbackRegistry::getInstance().registerCallbackFunction("LuaCommandCB", TheCommandCallback);
- CallbackRegistry::getInstance().registerCallbackFunction("LuaCharacterCB", TheCharacterCallback);
- }
-};
-static CallbackfunctionRegisterer Instance;
}
-// -----------------------------------------------------------------------------
-
-static InputEngine *GetIE() {
- Kernel *pKernel = Kernel::GetInstance();
+static InputEngine *getIE() {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- InputEngine *pIE = static_cast<InputEngine *>(pKernel->GetService("input"));
+ InputEngine *pIE = pKernel->getInput();
BS_ASSERT(pIE);
return pIE;
}
-// -----------------------------------------------------------------------------
-
-static int Init(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int init(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushbooleancpp(L, pIE->Init());
+ lua_pushbooleancpp(L, pIE->init());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int Update(lua_State *L) {
- InputEngine *pIE = GetIE();
-
- // Beim ersten Aufruf der Update()-Methode werden die beiden Callbacks am Input-Objekt registriert.
- // Dieses kann nicht in _RegisterScriptBindings() passieren, da diese Funktion vom Konstruktor der abstrakten Basisklasse aufgerufen wird und die
- // Register...()-Methoden abstrakt sind, im Konstruktor der Basisklasse also nicht aufgerufen werden können.
- static bool FirstCall = true;
- if (FirstCall) {
- FirstCall = false;
- pIE->RegisterCharacterCallback(TheCharacterCallback);
- pIE->RegisterCommandCallback(TheCommandCallback);
- }
+static int update(lua_State *L) {
+ InputEngine *pIE = getIE();
- pIE->Update();
+ pIE->update();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int IsLeftMouseDown(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int isLeftMouseDown(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushbooleancpp(L, pIE->IsLeftMouseDown());
+ lua_pushbooleancpp(L, pIE->isLeftMouseDown());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int IsRightMouseDown(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int isRightMouseDown(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushbooleancpp(L, pIE->IsRightMouseDown());
+ lua_pushbooleancpp(L, pIE->isRightMouseDown());
return 1;
}
-// -----------------------------------------------------------------------------
+static int wasLeftMouseDown(lua_State *L) {
+ InputEngine *pIE = getIE();
-static int WasLeftMouseDown(lua_State *L) {
- InputEngine *pIE = GetIE();
-
- lua_pushbooleancpp(L, pIE->WasLeftMouseDown());
+ lua_pushbooleancpp(L, pIE->wasLeftMouseDown());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int WasRightMouseDown(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int wasRightMouseDown(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushbooleancpp(L, pIE->WasRightMouseDown());
+ lua_pushbooleancpp(L, pIE->wasRightMouseDown());
return 1;
}
-// -----------------------------------------------------------------------------
+static int isLeftDoubleClick(lua_State *L) {
+ InputEngine *pIE = getIE();
-static int IsLeftDoubleClick(lua_State *L) {
- InputEngine *pIE = GetIE();
-
- lua_pushbooleancpp(L, pIE->IsLeftDoubleClick());
+ lua_pushbooleancpp(L, pIE->isLeftDoubleClick());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetMouseX(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int getMouseX(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushnumber(L, pIE->GetMouseX());
+ lua_pushnumber(L, pIE->getMouseX());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetMouseY(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int getMouseY(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushnumber(L, pIE->GetMouseY());
+ lua_pushnumber(L, pIE->getMouseY());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int IsKeyDown(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int isKeyDown(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushbooleancpp(L, pIE->IsKeyDown((uint) luaL_checknumber(L, 1)));
+ lua_pushbooleancpp(L, pIE->isKeyDown((uint)luaL_checknumber(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int WasKeyDown(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int wasKeyDown(lua_State *L) {
+ InputEngine *pIE = getIE();
- lua_pushbooleancpp(L, pIE->WasKeyDown((uint) luaL_checknumber(L, 1)));
+ lua_pushbooleancpp(L, pIE->wasKeyDown((uint)luaL_checknumber(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetMouseX(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int setMouseX(lua_State *L) {
+ InputEngine *pIE = getIE();
- pIE->SetMouseX((int) luaL_checknumber(L, 1));
+ pIE->setMouseX((int)luaL_checknumber(L, 1));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int SetMouseY(lua_State *L) {
- InputEngine *pIE = GetIE();
+static int setMouseY(lua_State *L) {
+ InputEngine *pIE = getIE();
- pIE->SetMouseY((int) luaL_checknumber(L, 1));
+ pIE->setMouseY((int)luaL_checknumber(L, 1));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static void TheCharacterCallback(int Character) {
- CharacterCallbackPtr->Character = static_cast<byte>(Character);
- lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
- CharacterCallbackPtr->invokeCallbackFunctions(L, 1);
+static void theCharacterCallback(int character) {
+ characterCallbackPtr->_character = static_cast<byte>(character);
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ characterCallbackPtr->invokeCallbackFunctions(L, 1);
}
-// -----------------------------------------------------------------------------
-
-static int RegisterCharacterCallback(lua_State *L) {
+static int registerCharacterCallback(lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
- CharacterCallbackPtr->registerCallbackFunction(L, 1);
+ characterCallbackPtr->registerCallbackFunction(L, 1);
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int UnregisterCharacterCallback(lua_State *L) {
+static int unregisterCharacterCallback(lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
- CharacterCallbackPtr->unregisterCallbackFunction(L, 1);
+ characterCallbackPtr->unregisterCallbackFunction(L, 1);
return 0;
}
-// -----------------------------------------------------------------------------
-
-static void TheCommandCallback(int Command) {
- CommandCallbackPtr->Command = static_cast<InputEngine::KEY_COMMANDS>(Command);
- lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
- CommandCallbackPtr->invokeCallbackFunctions(L, 1);
+static void theCommandCallback(int command) {
+ commandCallbackPtr->_command = static_cast<InputEngine::KEY_COMMANDS>(command);
+ lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
+ commandCallbackPtr->invokeCallbackFunctions(L, 1);
}
-// -----------------------------------------------------------------------------
-
-static int RegisterCommandCallback(lua_State *L) {
+static int registerCommandCallback(lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
- CommandCallbackPtr->registerCallbackFunction(L, 1);
+ commandCallbackPtr->registerCallbackFunction(L, 1);
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int UnregisterCommandCallback(lua_State *L) {
+static int unregisterCommandCallback(lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
- CommandCallbackPtr->unregisterCallbackFunction(L, 1);
+ commandCallbackPtr->unregisterCallbackFunction(L, 1);
return 0;
}
-// -----------------------------------------------------------------------------
-
static const char *PACKAGE_LIBRARY_NAME = "Input";
static const luaL_reg PACKAGE_FUNCTIONS[] = {
- {"Init", Init},
- {"Update", Update},
- {"IsLeftMouseDown", IsLeftMouseDown},
- {"IsRightMouseDown", IsRightMouseDown},
- {"WasLeftMouseDown", WasLeftMouseDown},
- {"WasRightMouseDown", WasRightMouseDown},
- {"IsLeftDoubleClick", IsLeftDoubleClick},
- {"GetMouseX", GetMouseX},
- {"GetMouseY", GetMouseY},
- {"SetMouseX", SetMouseX},
- {"SetMouseY", SetMouseY},
- {"IsKeyDown", IsKeyDown},
- {"WasKeyDown", WasKeyDown},
- {"RegisterCharacterCallback", RegisterCharacterCallback},
- {"UnregisterCharacterCallback", UnregisterCharacterCallback},
- {"RegisterCommandCallback", RegisterCommandCallback},
- {"UnregisterCommandCallback", UnregisterCommandCallback},
+ {"Init", init},
+ {"Update", update},
+ {"IsLeftMouseDown", isLeftMouseDown},
+ {"IsRightMouseDown", isRightMouseDown},
+ {"WasLeftMouseDown", wasLeftMouseDown},
+ {"WasRightMouseDown", wasRightMouseDown},
+ {"IsLeftDoubleClick", isLeftDoubleClick},
+ {"GetMouseX", getMouseX},
+ {"GetMouseY", getMouseY},
+ {"SetMouseX", setMouseX},
+ {"SetMouseY", setMouseY},
+ {"IsKeyDown", isKeyDown},
+ {"WasKeyDown", wasKeyDown},
+ {"RegisterCharacterCallback", registerCharacterCallback},
+ {"UnregisterCharacterCallback", unregisterCharacterCallback},
+ {"RegisterCommandCallback", registerCommandCallback},
+ {"UnregisterCommandCallback", unregisterCommandCallback},
{0, 0}
};
@@ -336,9 +264,9 @@ static const lua_constant_reg PACKAGE_CONSTANTS[] = {
// -----------------------------------------------------------------------------
bool InputEngine::registerScriptBindings() {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pScript = pKernel->getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
@@ -346,10 +274,24 @@ bool InputEngine::registerScriptBindings() {
if (!LuaBindhelper::addFunctionsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_FUNCTIONS)) return false;
if (!LuaBindhelper::addConstantsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_CONSTANTS)) return false;
- CharacterCallbackPtr = Common::SharedPtr<CharacterCallbackClass>(new CharacterCallbackClass(L));
- CommandCallbackPtr = Common::SharedPtr<CommandCallbackClass>(new CommandCallbackClass(L));
+ assert(characterCallbackPtr == 0);
+ characterCallbackPtr = new CharacterCallbackClass(L);
+
+ assert(commandCallbackPtr == 0);
+ commandCallbackPtr = new CommandCallbackClass(L);
+
+ setCharacterCallback(theCharacterCallback);
+ setCommandCallback(theCommandCallback);
return true;
}
+void InputEngine::unregisterScriptBindings() {
+ delete characterCallbackPtr;
+ characterCallbackPtr = 0;
+
+ delete commandCallbackPtr;
+ commandCallbackPtr = 0;
+}
+
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/callbackregistry.cpp b/engines/sword25/kernel/callbackregistry.cpp
deleted file mode 100644
index 32b2597334..0000000000
--- a/engines/sword25/kernel/callbackregistry.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-// Alle Callbackfunktionen die von Objekten gerufen werden, die persistiert werden können, müssen hier registriert werden.
-// Beim Speichern wird statt des Pointers der Bezeichner gespeichert. Beim Laden wird der Bezeichner wieder in einen Pointer umgewandelt.
-// Diese Klasse führt also so etwas ähnliches wie eine Importtabelle für Callback-Funktionen.
-//
-// Dieses Vorgehen hat mehrere Vorteile:
-// 1. Die Speicherstände sind plattformunabhängig. Es werden keine Pointer auf Funktionen gespeichert, sondern nur Namen von Callbackfunktionen.
-// Diese können beim Laden über diese Klasse in systemabhängige Pointer umgewandelt werden.
-// 2. Speicherstände können auch nach einem Engineupdate weiterhin benutzt werden. Beim Erstellen einer neun Binary verschieben sich häufig die
-// Funktionen. Eine Callbackfunktion könnte sich also nach einem Update an einer anderen Stelle befinden als davor. Wenn im Spielstand der
-// Pointer gespeichert war, stürtzt das Programm beim Äufrufen dieser Callbackfunktion ab. Durch das Auflösungverfahren wird beim Laden der
-// Callbackbezeichner in den neuen Funktionspointer umgewandelt und der Aufruf kann erfolgen.
-
-// -----------------------------------------------------------------------------
-// Logging
-// -----------------------------------------------------------------------------
-
-#define BS_LOG_PREFIX "CALLBACKREGISTRY"
-
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
-#include "sword25/kernel/callbackregistry.h"
-
-namespace Sword25 {
-
-// -----------------------------------------------------------------------------
-
-bool CallbackRegistry::registerCallbackFunction(const Common::String &name, CallbackPtr ptr) {
- if (name == "") {
- BS_LOG_ERRORLN("The empty string is not allowed as a callback function name.");
- return false;
- }
-
- if (findPtrByName(name) != 0) {
- BS_LOG_ERRORLN("There is already a callback function with the name \"%s\".", name.c_str());
- return false;
- }
- if (findNameByPtr(ptr) != "") {
- BS_LOG_ERRORLN("There is already a callback function with the pointer 0x%x.", ptr);
- return false;
- }
-
- storeCallbackFunction(name, ptr);
-
- return true;
-}
-
-// -----------------------------------------------------------------------------
-
-CallbackPtr CallbackRegistry::resolveCallbackFunction(const Common::String &name) const {
- CallbackPtr result = findPtrByName(name);
-
- if (!result) {
- BS_LOG_ERRORLN("There is no callback function with the name \"%s\".", name.c_str());
- }
-
- return result;
-}
-
-// -----------------------------------------------------------------------------
-
-Common::String CallbackRegistry::resolveCallbackPointer(CallbackPtr ptr) const {
- const Common::String &result = findNameByPtr(ptr);
-
- if (result == "") {
- BS_LOG_ERRORLN("There is no callback function with the pointer 0x%x.", ptr);
- }
-
- return result;
-}
-
-// -----------------------------------------------------------------------------
-
-CallbackPtr CallbackRegistry::findPtrByName(const Common::String &name) const {
- // Eintrag in der Map finden und den Pointer zurückgeben.
- NameToPtrMap::const_iterator it = _nameToPtrMap.find(name);
- return it == _nameToPtrMap.end() ? 0 : it->_value;
-}
-
-// -----------------------------------------------------------------------------
-
-Common::String CallbackRegistry::findNameByPtr(CallbackPtr ptr) const {
- // Eintrag in der Map finden und den Namen zurückgeben.
- PtrToNameMap::const_iterator it = _ptrToNameMap.find(ptr);
- return it == _ptrToNameMap.end() ? "" : it->_value;
-}
-
-// -----------------------------------------------------------------------------
-
-void CallbackRegistry::storeCallbackFunction(const Common::String &name, CallbackPtr ptr) {
- // Callback-Funktion in beide Maps eintragen.
- _nameToPtrMap[name] = ptr;
- _ptrToNameMap[ptr] = name;
-}
-
-} // End of namespace Sword25
diff --git a/engines/sword25/kernel/callbackregistry.h b/engines/sword25/kernel/callbackregistry.h
deleted file mode 100644
index c5076d22f5..0000000000
--- a/engines/sword25/kernel/callbackregistry.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-#ifndef SWORD25_CALLBACK_REGISTRY_H
-#define SWORD25_CALLBACK_REGISTRY_H
-
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
-#include "common/scummsys.h"
-#include "common/str.h"
-#include "common/hash-str.h"
-#include "common/hashmap.h"
-#include "sword25/kernel/bs_stdint.h"
-#include "sword25/kernel/common.h"
-
-namespace Sword25 {
-
-// -----------------------------------------------------------------------------
-// Klassendeklaration
-// -----------------------------------------------------------------------------
-
-typedef void (*CallbackPtr)(int command);
-
-class CallbackRegistry {
-public:
- static CallbackRegistry &getInstance() {
- static CallbackRegistry _instance;
- return _instance;
- }
-
- bool registerCallbackFunction(const Common::String &name, CallbackPtr ptr);
- CallbackPtr resolveCallbackFunction(const Common::String &name) const;
- Common::String resolveCallbackPointer(CallbackPtr ptr) const;
-
-private:
- typedef Common::HashMap<Common::String, CallbackPtr, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> NameToPtrMap;
- NameToPtrMap _nameToPtrMap;
-
- struct CallbackPtr_EqualTo {
- bool operator()(CallbackPtr x, CallbackPtr y) const {
- return x == y;
- }
- };
- struct CallbackPtr_Hash {
- uint operator()(CallbackPtr x) const {
- return static_cast<uint>((int64)x % ((int64)1 << sizeof(uint)));
- }
- };
-
- typedef Common::HashMap<CallbackPtr, Common::String, CallbackPtr_Hash, CallbackPtr_EqualTo> PtrToNameMap;
- PtrToNameMap _ptrToNameMap;
-
- CallbackPtr findPtrByName(const Common::String &name) const;
- Common::String findNameByPtr(CallbackPtr ptr) const;
- void storeCallbackFunction(const Common::String &name, CallbackPtr ptr);
-};
-
-} // End of namespace Sword25
-
-#endif
diff --git a/engines/sword25/kernel/filesystemutil.cpp b/engines/sword25/kernel/filesystemutil.cpp
index 853e6b247f..d05ac922e1 100644
--- a/engines/sword25/kernel/filesystemutil.cpp
+++ b/engines/sword25/kernel/filesystemutil.cpp
@@ -32,10 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/savefile.h"
@@ -47,106 +43,45 @@ namespace Sword25 {
#define BS_LOG_PREFIX "FILESYSTEMUTIL"
-// -----------------------------------------------------------------------------
-// Constants and utility functions
-// -----------------------------------------------------------------------------
-
-Common::String GetAbsolutePath(const Common::String &Path) {
- Common::FSNode node(Path);
+Common::String FileSystemUtil::getUserdataDirectory() {
+ // FIXME: This code is a hack which bypasses the savefile API,
+ // and should eventually be removed.
+ Common::String path = ConfMan.get("savepath");
- if (!node.exists()) {
- // An error has occurred finding the node
- // We can do nothing at this pointer than return an empty string
- BS_LOG_ERRORLN("A call to GetAbsolutePath failed.");
+ if (path.empty()) {
+ error("No save path has been defined");
return "";
}
- // Return the result
- return node.getPath();
+ // Return the path
+ return path;
}
-// -----------------------------------------------------------------------------
-// Class definitions
-// -----------------------------------------------------------------------------
-
-class BS_FileSystemUtilScummVM : public FileSystemUtil {
-public:
- virtual Common::String GetUserdataDirectory() {
- Common::String path = ConfMan.get("savepath");
-
- if (path.empty()) {
- error("No save path has been defined");
- return "";
- }
-
- // Return the path
- return path;
- }
-
- virtual Common::String GetPathSeparator() {
- return Common::String("/");
- }
-
- virtual int64 GetFileSize(const Common::String &Filename) {
- Common::FSNode node(Filename);
-
- // If the file does not exist, return -1 as a result
- if (!node.exists())
- return -1;
-
- // Get the size of the file and return it
- Common::File f;
- f.open(node);
- uint32 size = f.size();
- f.close();
-
- return size;
- }
-
- virtual TimeDate GetFileTime(const Common::String &Filename) {
- // TODO: There isn't any way in ScummVM to get a file's modified date/time. We will need to check
- // what code makes use of it. If it's only the save game code, for example, we may be able to
- // encode the date/time inside the savegame files themselves.
- TimeDate result;
- g_system->getTimeAndDate(result);
- return result;
- }
+Common::String FileSystemUtil::getPathSeparator() {
+ // FIXME: This code is a hack which bypasses the savefile API,
+ // and should eventually be removed.
+ return Common::String("/");
+}
- virtual bool FileExists(const Common::String &Filename) {
- Common::File f;
- if (f.exists(Filename))
- return true;
+bool FileSystemUtil::fileExists(const Common::String &filename) {
+ Common::File f;
+ if (f.exists(filename))
+ return true;
- // Check if the file exists in the save folder
- Common::FSNode folder(PersistenceService::GetSavegameDirectory());
- Common::FSNode fileNode = folder.getChild(FileSystemUtil::GetInstance().GetPathFilename(Filename));
- return fileNode.exists();
- }
-
- virtual bool CreateDirectory(const Common::String &DirectoryName) {
- // ScummVM doesn't support creating folders, so this is only a stub
- BS_LOG_ERRORLN("CreateDirectory method called");
- return false;
- }
+ // Check if the file exists in the save folder
+ Common::FSNode folder(PersistenceService::getSavegameDirectory());
+ Common::FSNode fileNode = folder.getChild(getPathFilename(filename));
+ return fileNode.exists();
+}
- virtual Common::String GetPathFilename(const Common::String &Path) {
- for (int i = Path.size() - 1; i >= 0; --i) {
- if ((Path[i] == '/') || (Path[i] == '\\')) {
- return Common::String(&Path.c_str()[i + 1]);
- }
+Common::String FileSystemUtil::getPathFilename(const Common::String &path) {
+ for (int i = path.size() - 1; i >= 0; --i) {
+ if ((path[i] == '/') || (path[i] == '\\')) {
+ return Common::String(&path.c_str()[i + 1]);
}
-
- return Path;
}
-};
-
-// -----------------------------------------------------------------------------
-// Singleton method of parent class
-// -----------------------------------------------------------------------------
-FileSystemUtil &FileSystemUtil::GetInstance() {
- static BS_FileSystemUtilScummVM Instance;
- return Instance;
+ return path;
}
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/filesystemutil.h b/engines/sword25/kernel/filesystemutil.h
index 43ce7c908e..38a3fdaa12 100644
--- a/engines/sword25/kernel/filesystemutil.h
+++ b/engines/sword25/kernel/filesystemutil.h
@@ -38,7 +38,7 @@
* operations that do not have equivalents in the C/C++ libraries.
*
* Each supported platform must implement this interface, and the method
- * BS_FileSystemUtil Singleton::getInstance()
+ * BS_FileSystemUtil Singleton::instance()
*/
#ifndef SWORD25_FILESYSTEMUTIL_H
@@ -52,7 +52,6 @@
#include "common/str.h"
#include "common/str-array.h"
#include "sword25/kernel/common.h"
-#include "sword25/kernel/bs_stdint.h"
namespace Sword25 {
@@ -62,8 +61,6 @@ namespace Sword25 {
class FileSystemUtil {
public:
- static FileSystemUtil &GetInstance();
- virtual ~FileSystemUtil() {};
/**
* This function returns the name of the directory in which all user data is to be stored.
@@ -71,42 +68,32 @@ public:
* These are for example Screenshots, game saves, configuration files, log files, ...
* @return Returns the name of the directory for user data.
*/
- virtual Common::String GetUserdataDirectory() = 0;
+ static Common::String getUserdataDirectory();
+
/**
* @return Returns the path seperator
*/
- virtual Common::String GetPathSeparator() = 0;
+ static Common::String getPathSeparator();
+
/**
* @param Filename The path to a file.
* @return Returns the size of the specified file. If the size could not be
* determined, or the file does not exist, returns -1
*/
- virtual int64 GetFileSize(const Common::String &Filename) = 0;
- /**
- * @param Filename The path to a file.
- * @return Returns the timestamp of the specified file.
- */
- virtual TimeDate GetFileTime(const Common::String &Filename) = 0;
+ static int32 getFileSize(const Common::String &filename);
+
/**
* @param Filename The path to a file.
* @return Returns true if the file exists.
*/
- virtual bool FileExists(const Common::String &Filename) = 0;
- /**
- * This function creates a directory
- *
- * If the parameter is "\b\c\d\e" is passed, and "\b\c" already exists, then folder 'd'
- * will be created, and subdirectory 'e' under it.
- * @param DirectoryName The name of the directory to be created
- * @return Returns true if the folder(s) could be created, otherwise false.
- */
- virtual bool CreateDirectory(const Common::String &DirectoryName) = 0;
+ static bool fileExists(const Common::String &filename);
+
/**
* Gets the filename from a path and filename
* @param Filename The full path and filename
* @return Returns just the filename
*/
- virtual Common::String GetPathFilename(const Common::String &Path) = 0;
+ static Common::String getPathFilename(const Common::String &path);
};
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/inputpersistenceblock.cpp b/engines/sword25/kernel/inputpersistenceblock.cpp
index b51b1037a7..652c2f362f 100644
--- a/engines/sword25/kernel/inputpersistenceblock.cpp
+++ b/engines/sword25/kernel/inputpersistenceblock.cpp
@@ -34,146 +34,116 @@
#define BS_LOG_PREFIX "INPUTPERSISTENCEBLOCK"
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/inputpersistenceblock.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Constructor / Destructor
-// -----------------------------------------------------------------------------
-
-InputPersistenceBlock::InputPersistenceBlock(const void *Data, uint DataLength) :
- m_Data(static_cast<const byte *>(Data), DataLength),
- m_ErrorState(NONE) {
- m_Iter = m_Data.begin();
+InputPersistenceBlock::InputPersistenceBlock(const void *data, uint dataLength) :
+ _data(static_cast<const byte *>(data), dataLength),
+ _errorState(NONE) {
+ _iter = _data.begin();
}
-// -----------------------------------------------------------------------------
-
InputPersistenceBlock::~InputPersistenceBlock() {
- if (m_Iter != m_Data.end()) BS_LOG_WARNINGLN("Persistence block was not read to the end.");
+ if (_iter != _data.end())
+ BS_LOG_WARNINGLN("Persistence block was not read to the end.");
}
-// -----------------------------------------------------------------------------
-// Reading
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::read(int16 &Value) {
+void InputPersistenceBlock::read(int16 &value) {
signed int v;
read(v);
- Value = static_cast<int16>(v);
+ value = static_cast<int16>(v);
}
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::read(signed int &Value) {
- if (CheckMarker(SINT_MARKER)) {
- RawRead(&Value, sizeof(signed int));
- Value = ConvertEndianessFromStorageToSystem(Value);
+void InputPersistenceBlock::read(signed int &value) {
+ if (checkMarker(SINT_MARKER)) {
+ rawRead(&value, sizeof(signed int));
+ value = convertEndianessFromStorageToSystem(value);
} else {
- Value = 0;
+ value = 0;
}
}
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::read(uint &Value) {
- if (CheckMarker(UINT_MARKER)) {
- RawRead(&Value, sizeof(uint));
- Value = ConvertEndianessFromStorageToSystem(Value);
+void InputPersistenceBlock::read(uint &value) {
+ if (checkMarker(UINT_MARKER)) {
+ rawRead(&value, sizeof(uint));
+ value = convertEndianessFromStorageToSystem(value);
} else {
- Value = 0;
+ value = 0;
}
}
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::read(float &Value) {
- if (CheckMarker(FLOAT_MARKER)) {
- RawRead(&Value, sizeof(float));
- Value = ConvertEndianessFromStorageToSystem(Value);
+void InputPersistenceBlock::read(float &value) {
+ if (checkMarker(FLOAT_MARKER)) {
+ rawRead(&value, sizeof(float));
+ value = convertEndianessFromStorageToSystem(value);
} else {
- Value = 0.0f;
+ value = 0.0f;
}
}
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::read(bool &Value) {
- if (CheckMarker(BOOL_MARKER)) {
- uint UIntBool;
- RawRead(&UIntBool, sizeof(float));
- UIntBool = ConvertEndianessFromStorageToSystem(UIntBool);
- Value = UIntBool == 0 ? false : true;
+void InputPersistenceBlock::read(bool &value) {
+ if (checkMarker(BOOL_MARKER)) {
+ uint uintBool;
+ rawRead(&uintBool, sizeof(float));
+ uintBool = convertEndianessFromStorageToSystem(uintBool);
+ value = uintBool == 0 ? false : true;
} else {
- Value = 0.0f;
+ value = 0.0f;
}
}
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::read(Common::String &Value) {
- Value = "";
+void InputPersistenceBlock::readString(Common::String &value) {
+ value = "";
- if (CheckMarker(STRING_MARKER)) {
- uint Size;
- read(Size);
+ if (checkMarker(STRING_MARKER)) {
+ uint size;
+ read(size);
- if (CheckBlockSize(Size)) {
- Value = Common::String(reinterpret_cast<const char *>(&*m_Iter), Size);
- m_Iter += Size;
+ if (checkBlockSize(size)) {
+ value = Common::String(reinterpret_cast<const char *>(&*_iter), size);
+ _iter += size;
}
}
}
-// -----------------------------------------------------------------------------
+void InputPersistenceBlock::readByteArray(Common::Array<byte> &value) {
+ if (checkMarker(BLOCK_MARKER)) {
+ uint size;
+ read(size);
-void InputPersistenceBlock::read(Common::Array<byte> &Value) {
- if (CheckMarker(BLOCK_MARKER)) {
- uint Size;
- read(Size);
-
- if (CheckBlockSize(Size)) {
- Value = Common::Array<byte>(m_Iter, Size);
- m_Iter += Size;
+ if (checkBlockSize(size)) {
+ value = Common::Array<byte>(_iter, size);
+ _iter += size;
}
}
}
-// -----------------------------------------------------------------------------
-
-void InputPersistenceBlock::RawRead(void *DestPtr, size_t Size) {
- if (CheckBlockSize(Size)) {
- memcpy(DestPtr, &*m_Iter, Size);
- m_Iter += Size;
+void InputPersistenceBlock::rawRead(void *destPtr, size_t size) {
+ if (checkBlockSize(size)) {
+ memcpy(destPtr, &*_iter, size);
+ _iter += size;
}
}
-// -----------------------------------------------------------------------------
-
-bool InputPersistenceBlock::CheckBlockSize(int Size) {
- if (m_Data.end() - m_Iter >= Size) {
+bool InputPersistenceBlock::checkBlockSize(int size) {
+ if (_data.end() - _iter >= size) {
return true;
} else {
- m_ErrorState = END_OF_DATA;
+ _errorState = END_OF_DATA;
BS_LOG_ERRORLN("Unexpected end of persistence block.");
return false;
}
}
-// -----------------------------------------------------------------------------
-
-bool InputPersistenceBlock::CheckMarker(byte Marker) {
- if (!isGood() || !CheckBlockSize(1)) return false;
+bool InputPersistenceBlock::checkMarker(byte marker) {
+ if (!isGood() || !checkBlockSize(1))
+ return false;
- if (*m_Iter++ == Marker) {
+ if (*_iter++ == marker) {
return true;
} else {
- m_ErrorState = OUT_OF_SYNC;
+ _errorState = OUT_OF_SYNC;
BS_LOG_ERRORLN("Wrong type marker found in persistence block.");
return false;
}
diff --git a/engines/sword25/kernel/inputpersistenceblock.h b/engines/sword25/kernel/inputpersistenceblock.h
index a6978e5899..f6ab256460 100644
--- a/engines/sword25/kernel/inputpersistenceblock.h
+++ b/engines/sword25/kernel/inputpersistenceblock.h
@@ -35,20 +35,12 @@
#ifndef SWORD25_INPUTPERSISTENCEBLOCK_H
#define SWORD25_INPUTPERSISTENCEBLOCK_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "common/array.h"
#include "sword25/kernel/common.h"
#include "sword25/kernel/persistenceblock.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Class declaration
-// -----------------------------------------------------------------------------
-
class InputPersistenceBlock : public PersistenceBlock {
public:
enum ErrorState {
@@ -57,32 +49,32 @@ public:
OUT_OF_SYNC
};
- InputPersistenceBlock(const void *Data, uint DataLength);
+ InputPersistenceBlock(const void *data, uint dataLength);
virtual ~InputPersistenceBlock();
- void read(int16 &Value);
- void read(signed int &Value);
- void read(uint &Value);
- void read(float &Value);
- void read(bool &Value);
- void read(Common::String &Value);
- void read(Common::Array<byte> &Value);
+ void read(int16 &value);
+ void read(signed int &value);
+ void read(uint &value);
+ void read(float &value);
+ void read(bool &value);
+ void readString(Common::String &value);
+ void readByteArray(Common::Array<byte> &value);
bool isGood() const {
- return m_ErrorState == NONE;
+ return _errorState == NONE;
}
- ErrorState GetErrorState() const {
- return m_ErrorState;
+ ErrorState getErrorState() const {
+ return _errorState;
}
private:
- bool CheckMarker(byte Marker);
- bool CheckBlockSize(int Size);
- void RawRead(void *DestPtr, size_t Size);
+ bool checkMarker(byte marker);
+ bool checkBlockSize(int size);
+ void rawRead(void *destPtr, size_t size);
- Common::Array<byte> m_Data;
- Common::Array<byte>::const_iterator m_Iter;
- ErrorState m_ErrorState;
+ Common::Array<byte> _data;
+ Common::Array<byte>::const_iterator _iter;
+ ErrorState _errorState;
};
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/kernel.cpp b/engines/sword25/kernel/kernel.cpp
index 3e7e7f125f..9bd8ac5ff7 100644
--- a/engines/sword25/kernel/kernel.cpp
+++ b/engines/sword25/kernel/kernel.cpp
@@ -38,417 +38,174 @@
#include "sword25/input/inputengine.h"
#include "sword25/kernel/kernel.h"
#include "sword25/kernel/persistenceservice.h"
-#include "sword25/kernel/service_ids.h"
+#include "sword25/math/geometry.h"
#include "sword25/package/packagemanager.h"
-#include "sword25/script/script.h"
+#include "sword25/script/luascript.h"
#include "sword25/sfx/soundengine.h"
namespace Sword25 {
#define BS_LOG_PREFIX "KERNEL"
-Kernel *Kernel::_Instance = 0;
+Kernel *Kernel::_instance = 0;
Kernel::Kernel() :
- _pWindow(NULL),
- _Running(false),
- _pResourceManager(NULL),
- _InitSuccess(false) {
+ _resourceManager(NULL),
+ _initSuccess(false),
+ _gfx(0),
+ _sfx(0),
+ _input(0),
+ _package(0),
+ _script(0),
+ _fmv(0)
+ {
// Log that the kernel is beign created
BS_LOGLN("created.");
-
- // Read the BS_SERVICE_TABLE and prepare kernel structures
- for (uint i = 0; i < BS_SERVICE_COUNT; i++) {
- // Is the superclass already registered?
- Superclass *pCurSuperclass = NULL;
- Common::Array<Superclass *>::iterator Iter;
- for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter)
- if ((*Iter)->GetIdentifier() == BS_SERVICE_TABLE[i].SuperclassIdentifier) {
- pCurSuperclass = *Iter;
- break;
- }
-
- // If the superclass isn't already registered, then add it in
- if (!pCurSuperclass)
- _SuperclassList.push_back(new Superclass(this, BS_SERVICE_TABLE[i].SuperclassIdentifier));
- }
-
- // Create window object
- _pWindow = Window::CreateBSWindow(0, 0, 0, 0, false);
- if (!_pWindow) {
- BS_LOG_ERRORLN("Failed to create the window.");
- } else
- BS_LOGLN("Window created.");
+ _instance = this;
// Create the resource manager
- _pResourceManager = new ResourceManager(this);
+ _resourceManager = new ResourceManager(this);
// Initialise the script engine
- ScriptEngine *pScript = static_cast<ScriptEngine *>(NewService("script", "lua"));
- if (!pScript || !pScript->init()) {
- _InitSuccess = false;
+ _script = new LuaScriptEngine(this);
+ if (!_script || !_script->init()) {
+ _initSuccess = false;
return;
}
// Register kernel script bindings
- if (!_RegisterScriptBindings()) {
+ if (!registerScriptBindings()) {
BS_LOG_ERRORLN("Script bindings could not be registered.");
- _InitSuccess = false;
+ _initSuccess = false;
return;
}
BS_LOGLN("Script bindings registered.");
- _InitSuccess = true;
-}
-
-Kernel::~Kernel() {
- // Services are de-registered in reverse order of creation
- while (!_ServiceCreationOrder.empty()) {
- Superclass *superclass = GetSuperclassByIdentifier(_ServiceCreationOrder.top());
- if (superclass) superclass->DisconnectService();
- _ServiceCreationOrder.pop();
- }
-
- // Empty the Superclass list
- while (_SuperclassList.size()) {
- delete _SuperclassList.back();
- _SuperclassList.pop_back();
- }
-
- // Release the window object
- delete _pWindow;
- BS_LOGLN("Window destroyed.");
-
- // Resource-Manager freigeben
- delete _pResourceManager;
-
- BS_LOGLN("destroyed.");
-}
-
-// Service Methoden
-// ----------------
-
-Kernel::Superclass::Superclass(Kernel *pKernel, const Common::String &Identifier) :
- _pKernel(pKernel),
- _Identifier(Identifier),
- _ServiceCount(0),
- _ActiveService(NULL) {
- for (uint i = 0; i < BS_SERVICE_COUNT; i++)
- if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier)
- _ServiceCount++;
-}
-
-Kernel::Superclass::~Superclass() {
- DisconnectService();
-}
-
-/**
- * Gets the identifier of a service with a given superclass.
- * The number of services in a superclass can be learned with GetServiceCount().
- * @param SuperclassIdentifier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- * @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
- * Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
- * 0 und GetServiceCount() - 1 sein.
- */
-Common::String Kernel::Superclass::GetServiceIdentifier(uint Number) {
- if (Number > _ServiceCount) return NULL;
-
- uint CurServiceOrd = 0;
- for (uint i = 0; i < BS_SERVICE_COUNT; i++) {
- if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier) {
- if (Number == CurServiceOrd)
- return BS_SERVICE_TABLE[i].ServiceIdentifier;
- else
- CurServiceOrd++;
- }
- }
-
- return Common::String("");
-}
-
-/**
- * Creates a new service with the given identifier. Returns a pointer to the service, or null if the
- * service could not be created
- * Note: All services must be registered in service_ids.h, otherwise they cannot be created here
- * @param SuperclassIdentifier The name of the superclass of the service
- * z.B: "sfx", "gfx", "package" ...
- * @param ServiceIdentifier The name of the service
- * For the superclass "sfx" an example could be "Fmod" or "directsound"
- */
-Service *Kernel::Superclass::NewService(const Common::String &ServiceIdentifier) {
- for (uint i = 0; i < BS_SERVICE_COUNT; i++)
- if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier &&
- BS_SERVICE_TABLE[i].ServiceIdentifier == ServiceIdentifier) {
- Service *NewService_ = BS_SERVICE_TABLE[i].CreateMethod(_pKernel);
-
- if (NewService_) {
- DisconnectService();
- BS_LOGLN("Service '%s' created from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
- _ActiveService = NewService_;
- _ActiveServiceName = BS_SERVICE_TABLE[i].ServiceIdentifier;
- return _ActiveService;
- } else {
- BS_LOG_ERRORLN("Failed to create service '%s' from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
- return NULL;
- }
- }
-
- BS_LOG_ERRORLN("Service '%s' is not avaliable from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
- return NULL;
-}
-
-/**
- * Ends the current service of a superclass. Returns true on success, and false if the superclass
- * does not exist or if not service was active
- * @param SuperclassIdentfier The name of the superclass which is to be disconnected
- * z.B: "sfx", "gfx", "package" ...
- */
-bool Kernel::Superclass::DisconnectService() {
- if (_ActiveService) {
- delete _ActiveService;
- _ActiveService = 0;
- BS_LOGLN("Active service '%s' disconnected from superclass '%s'.", _ActiveServiceName.c_str(), _Identifier.c_str());
- return true;
- }
-
- return false;
-}
-
-Kernel::Superclass *Kernel::GetSuperclassByIdentifier(const Common::String &Identifier) {
- Common::Array<Superclass *>::iterator Iter;
- for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) {
- if ((*Iter)->GetIdentifier() == Identifier)
- return *Iter;
- }
-
- // BS_LOG_ERRORLN("Superclass '%s' does not exist.", Identifier.c_str());
- return NULL;
-}
-
-/**
- * Returns the number of register superclasses
- */
-uint Kernel::GetSuperclassCount() {
- return _SuperclassList.size();
-}
-
-/**
- * Returns the name of a superclass with the specified index.
- * Note: The number of superclasses can be retrieved using GetSuperclassCount
- * @param Number The number of the superclass to return the identifier for.
- * It should be noted that the number should be between 0 und GetSuperclassCount() - 1.
- */
-Common::String Kernel::GetSuperclassIdentifier(uint Number) {
- if (Number > _SuperclassList.size()) return NULL;
+ _input = new InputEngine(this);
+ assert(_input);
- uint CurSuperclassOrd = 0;
- Common::Array<Superclass *>::iterator Iter;
- for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) {
- if (CurSuperclassOrd == Number)
- return ((*Iter)->GetIdentifier());
+ _gfx = new GraphicEngine(this);
+ assert(_gfx);
- CurSuperclassOrd++;
- }
+ _sfx = new SoundEngine(this);
+ assert(_sfx);
- return Common::String("");
-}
+ _package = new PackageManager(this);
+ assert(_package);
-/**
- * Returns the number of services registered with a given superclass
- * @param SuperclassIdentifier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- */
-uint Kernel::GetServiceCount(const Common::String &SuperclassIdentifier) {
- Superclass *pSuperclass;
- if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier)))
- return 0;
+ _geometry = new Geometry(this);
+ assert(_geometry);
- return pSuperclass->GetServiceCount();
+#ifdef USE_THEORADEC
+ _fmv = new MoviePlayer(this);
+ assert(_fmv);
+#endif
+ _initSuccess = true;
}
-/**
- * Gets the identifier of a service with a given superclass.
- * The number of services in a superclass can be learned with GetServiceCount().
- * @param SuperclassIdentifier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- * @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
- * Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
- * 0 und GetServiceCount() - 1 sein.
- */
-Common::String Kernel::GetServiceIdentifier(const Common::String &SuperclassIdentifier, uint Number) {
- Superclass *pSuperclass;
- if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
-
- return (pSuperclass->GetServiceIdentifier(Number));
-}
+Kernel::~Kernel() {
+ // Services are de-registered in reverse order of creation
-/**
- * Creates a new service with the given identifier. Returns a pointer to the service, or null if the
- * service could not be created
- * Note: All services must be registered in service_ids.h, otherwise they cannot be created here
- * @param SuperclassIdentifier The name of the superclass of the service
- * z.B: "sfx", "gfx", "package" ...
- * @param ServiceIdentifier The name of the service
- * For the superclass "sfx" an example could be "Fmod" or "directsound"
- */
-Service *Kernel::NewService(const Common::String &SuperclassIdentifier, const Common::String &ServiceIdentifier) {
- Superclass *pSuperclass;
- if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+ delete _input;
+ _input = 0;
- // Die Reihenfolge merken, in der Services erstellt werden, damit sie später in umgekehrter Reihenfolge entladen werden können.
- _ServiceCreationOrder.push(SuperclassIdentifier);
+ delete _gfx;
+ _gfx = 0;
- return pSuperclass->NewService(ServiceIdentifier);
-}
+ delete _sfx;
+ _sfx = 0;
-/**
- * Ends the current service of a superclass. Returns true on success, and false if the superclass
- * does not exist or if not service was active
- * @param SuperclassIdentfier The name of the superclass which is to be disconnected
- * z.B: "sfx", "gfx", "package" ...
- */
-bool Kernel::DisconnectService(const Common::String &SuperclassIdentifier) {
- Superclass *pSuperclass;
- if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return false;
+ delete _package;
+ _package = 0;
- return pSuperclass->DisconnectService();
-}
+ delete _geometry;
+ _geometry = 0;
-/**
- * Returns a pointer to the currently active service object of a superclass
- * @param SuperclassIdentfier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- */
-Service *Kernel::GetService(const Common::String &SuperclassIdentifier) {
- Superclass *pSuperclass;
- if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+#ifdef USE_THEORADEC
+ delete _fmv;
+ _fmv = 0;
+#endif
- return (pSuperclass->GetActiveService());
-}
+ delete _script;
+ _script = 0;
-/**
- * Returns the name of the currentl active service object of a superclass.
- * If an error occurs, then an empty string is returned
- * @param SuperclassIdentfier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- */
-Common::String Kernel::GetActiveServiceIdentifier(const Common::String &SuperclassIdentifier) {
- Superclass *pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier);
- if (!pSuperclass) return Common::String("");
+ // Resource-Manager freigeben
+ delete _resourceManager;
- return (pSuperclass->GetActiveServiceName());
+ BS_LOGLN("destroyed.");
}
-// -----------------------------------------------------------------------------
-
/**
* Returns a random number
* @param Min The minimum allowed value
* @param Max The maximum allowed value
*/
-int Kernel::GetRandomNumber(int Min, int Max) {
- BS_ASSERT(Min <= Max);
+int Kernel::getRandomNumber(int min, int max) {
+ BS_ASSERT(min <= max);
- return Min + _rnd.getRandomNumber(Max - Min + 1);
+ return min + _rnd.getRandomNumber(max - min + 1);
}
/**
* Returns the elapsed time since startup in milliseconds
*/
-uint Kernel::GetMilliTicks() {
+uint Kernel::getMilliTicks() {
return g_system->getMillis();
}
/**
- * Returns the elapsed time since the system start in microseconds.
- * This method should be used only if GetMilliTick() for the desired application is inaccurate.
- */
-uint64 Kernel::GetMicroTicks() {
- return g_system->getMillis() * 1000;
-}
-
-// Other methods
-// -----------------
-
-/**
* Returns how much memory is being used
*/
-size_t Kernel::GetUsedMemory() {
+size_t Kernel::getUsedMemory() {
return 0;
-
-#ifdef SCUMMVM_DISABLED_CODE
- PROCESS_MEMORY_COUNTERS pmc;
- pmc.cb = sizeof(pmc);
- if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
- return pmc.WorkingSetSize;
- } else {
- BS_LOG_ERRORLN("Call to GetProcessMemoryInfo() failed. Error code: %d", GetLastError());
- return 0;
- }
-#endif
}
-// -----------------------------------------------------------------------------
-
/**
- * Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active
+ * Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active.
*/
-GraphicEngine *Kernel::GetGfx() {
- return static_cast<GraphicEngine *>(GetService("gfx"));
+GraphicEngine *Kernel::getGfx() {
+ return _gfx;
}
-// -----------------------------------------------------------------------------
-
/**
- * Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active
+ * Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active.
*/
-SoundEngine *Kernel::GetSfx() {
- return static_cast<SoundEngine *>(GetService("sfx"));
+SoundEngine *Kernel::getSfx() {
+ return _sfx;
}
-// -----------------------------------------------------------------------------
-
/**
- * Returns a pointer to the active input service, or NULL if no input service is active
+ * Returns a pointer to the active input service, or NULL if no input service is active.
*/
-InputEngine *Kernel::GetInput() {
- return static_cast<InputEngine *>(GetService("input"));
+InputEngine *Kernel::getInput() {
+ return _input;
}
-// -----------------------------------------------------------------------------
-
/**
- * Returns a pointer to the active package manager, or NULL if no manager is active
+ * Returns a pointer to the active package manager, or NULL if no manager is active.
*/
-PackageManager *Kernel::GetPackage() {
- return static_cast<PackageManager *>(GetService("package"));
+PackageManager *Kernel::getPackage() {
+ return _package;
}
-// -----------------------------------------------------------------------------
-
/**
- * Returns a pointer to the script engine, or NULL if it is not active
+ * Returns a pointer to the script engine, or NULL if it is not active.
*/
-ScriptEngine *Kernel::GetScript() {
- return static_cast<ScriptEngine *>(GetService("script"));
+ScriptEngine *Kernel::getScript() {
+ return _script;
}
-// -----------------------------------------------------------------------------
-
/**
- * Returns a pointer to the movie player, or NULL if it is not active
+ * Returns a pointer to the movie player, or NULL if it is not active.
*/
-MoviePlayer *Kernel::GetFMV() {
- return static_cast<MoviePlayer *>(GetService("fmv"));
+MoviePlayer *Kernel::getFMV() {
+ return _fmv;
}
-// -----------------------------------------------------------------------------
-
-void Kernel::Sleep(uint Msecs) const {
- g_system->delayMillis(Msecs);
+void Kernel::sleep(uint msecs) const {
+ g_system->delayMillis(msecs);
}
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/kernel.h b/engines/sword25/kernel/kernel.h
index 55a64c783f..252de64e80 100644
--- a/engines/sword25/kernel/kernel.h
+++ b/engines/sword25/kernel/kernel.h
@@ -45,7 +45,6 @@
#ifndef SWORD25_KERNEL_H
#define SWORD25_KERNEL_H
-// Includes
#include "common/scummsys.h"
#include "common/random.h"
#include "common/stack.h"
@@ -53,14 +52,13 @@
#include "engines/engine.h"
#include "sword25/kernel/common.h"
-#include "sword25/kernel/bs_stdint.h"
-#include "sword25/kernel/window.h"
#include "sword25/kernel/resmanager.h"
namespace Sword25 {
// Class definitions
class Service;
+class Geometry;
class GraphicEngine;
class ScriptEngine;
class SoundEngine;
@@ -76,154 +74,73 @@ class MoviePlayer;
*/
class Kernel {
public:
- // Window methods
- // ----------------
-
- /**
- * Returns a pointer to the window object
- */
- Window *GetWindow() {
- return _pWindow;
- }
-
- // Service Methods
- // ---------------
-
- /**
- * Creates a new service with the given identifier. Returns a pointer to the service, or null if the
- * service could not be created
- * Note: All services must be registered in service_ids.h, otherwise they cannot be created here
- * @param SuperclassIdentifier The name of the superclass of the service
- * z.B: "sfx", "gfx", "package" ...
- * @param ServiceIdentifier The name of the service
- * For the superclass "sfx" an example could be "Fmod" or "directsound"
- */
- Service *NewService(const Common::String &SuperclassIdentifier, const Common::String &ServiceIdentifier);
-
- /**
- * Ends the current service of a superclass. Returns true on success, and false if the superclass
- * does not exist or if not service was active
- * @param SuperclassIdentfier The name of the superclass which is to be disconnected
- * z.B: "sfx", "gfx", "package" ...
- */
- bool DisconnectService(const Common::String &SuperclassIdentifier);
-
- /**
- * Returns a pointer to the currently active service object of a superclass
- * @param SuperclassIdentfier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- */
- Service *GetService(const Common::String &SuperclassIdentifier);
-
- /**
- * Returns the name of the currentl active service object of a superclass.
- * If an error occurs, then an empty string is returned
- * @param SuperclassIdentfier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- */
- Common::String GetActiveServiceIdentifier(const Common::String &SuperclassIdentifier);
-
- /**
- * Returns the number of register superclasses
- */
- uint GetSuperclassCount();
-
- /**
- * Returns the name of a superclass with the specified index.
- * Note: The number of superclasses can be retrieved using GetSuperclassCount
- * @param Number The number of the superclass to return the identifier for.
- * It should be noted that the number should be between 0 und GetSuperclassCount() - 1.
- */
- Common::String GetSuperclassIdentifier(uint Number);
-
- /**
- * Returns the number of services registered with a given superclass
- * @param SuperclassIdentifier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- */
- uint GetServiceCount(const Common::String &SuperclassIdentifier);
-
- /**
- * Gets the identifier of a service with a given superclass.
- * The number of services in a superclass can be learned with GetServiceCount().
- * @param SuperclassIdentifier The name of the superclass
- * z.B: "sfx", "gfx", "package" ...
- * @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
- * Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
- * 0 und GetServiceCount() - 1 sein.
- */
- Common::String GetServiceIdentifier(const Common::String &SuperclassIdentifier, uint Number);
/**
* Returns the elapsed time since startup in milliseconds
*/
- uint GetMilliTicks();
-
- /**
- * Returns the elapsed time since the system start in microseconds.
- * This method should be used only if GetMilliTick() for the desired application is inaccurate.
- */
- uint64 GetMicroTicks();
+ uint getMilliTicks();
/**
* Specifies whether the kernel was successfully initialised
*/
- bool GetInitSuccess() {
- return _InitSuccess;
+ bool getInitSuccess() const {
+ return _initSuccess;
}
/**
* Returns a pointer to the BS_ResourceManager
*/
- ResourceManager *GetResourceManager() {
- return _pResourceManager;
+ ResourceManager *getResourceManager() {
+ return _resourceManager;
}
/**
* Returns how much memory is being used
*/
- size_t GetUsedMemory();
+ size_t getUsedMemory();
/**
* Returns a random number
* @param Min The minimum allowed value
* @param Max The maximum allowed value
*/
- int GetRandomNumber(int Min, int Max);
+ int getRandomNumber(int min, int max);
/**
* Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active
*/
- GraphicEngine *GetGfx();
+ GraphicEngine *getGfx();
/**
* Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active
*/
- SoundEngine *GetSfx();
+ SoundEngine *getSfx();
/**
* Returns a pointer to the active input service, or NULL if no input service is active
*/
- InputEngine *GetInput();
+ InputEngine *getInput();
/**
* Returns a pointer to the active package manager, or NULL if no manager is active
*/
- PackageManager *GetPackage();
+ PackageManager *getPackage();
/**
* Returns a pointer to the script engine, or NULL if it is not active
*/
- ScriptEngine *GetScript();
+ ScriptEngine *getScript();
+
/**
* Returns a pointer to the movie player, or NULL if it is not active
*/
- MoviePlayer *GetFMV();
+ MoviePlayer *getFMV();
/**
* Pauses for the specified amount of time
* @param Msecs The amount of time in milliseconds
*/
- void Sleep(uint Msecs) const;
+ void sleep(uint msecs) const;
/**
* Returns the singleton instance for the kernel
*/
- static Kernel *GetInstance() {
- if (!_Instance) _Instance = new Kernel();
- return _Instance;
+ static Kernel *getInstance() {
+ if (!_instance)
+ _instance = new Kernel();
+ return _instance;
}
/**
@@ -231,18 +148,18 @@ public:
* This method should only be called when the game is ended. No subsequent calls to any kernel
* methods should be done after calling this method.
*/
- static void DeleteInstance() {
- if (_Instance) {
- delete _Instance;
- _Instance = NULL;
+ static void deleteInstance() {
+ if (_instance) {
+ delete _instance;
+ _instance = NULL;
}
}
/**
* Raises an error. This method is used in crashing testing.
*/
- void Crash() const {
- error("BS_Kernel::Crash");
+ void crash() const {
+ error("Kernel::Crash");
}
private:
@@ -257,114 +174,29 @@ private:
// -----------------------------------------------------------------------------
// Singleton instance
// -----------------------------------------------------------------------------
- static Kernel *_Instance;
-
- // Superclass class
- // ----------------
- class Superclass {
- private:
- Kernel *_pKernel;
- uint _ServiceCount;
- Common::String _Identifier;
- Service *_ActiveService;
- Common::String _ActiveServiceName;
-
- public:
- Superclass(Kernel *pKernel, const Common::String &Identifier);
- ~Superclass();
-
- uint GetServiceCount() const {
- return _ServiceCount;
- }
- Common::String GetIdentifier() const {
- return _Identifier;
- }
- Service *GetActiveService() const {
- return _ActiveService;
- }
- Common::String GetActiveServiceName() const {
- return _ActiveServiceName;
- }
- Common::String GetServiceIdentifier(uint Number);
- Service *NewService(const Common::String &ServiceIdentifier);
- bool DisconnectService();
- };
+ static Kernel *_instance;
- Common::Array<Superclass *> _SuperclassList;
- Common::Stack<Common::String> _ServiceCreationOrder;
- Superclass *GetSuperclassByIdentifier(const Common::String &Identifier);
-
- bool _InitSuccess; // Specifies whether the engine was set up correctly
- bool _Running; // Specifies whether the application should keep running on the next main loop iteration
-
- // Active window
- // -------------
- Window *_pWindow;
+ bool _initSuccess; // Specifies whether the engine was set up correctly
// Random number generator
// -----------------------
Common::RandomSource _rnd;
- /*
- // Features variables and methods
- // ----------------------------------
- enum _CPU_FEATURES_BITMASKS
- {
- _MMX_BITMASK = (1 << 23),
- _SSE_BITMASK = (1 << 25),
- _SSE2_BITMASK = (1 << 26),
- _3DNOW_BITMASK = (1 << 30),
- _3DNOWEXT_BITMASK = (1 << 31)
- };
-
- bool _DetectCPU();
-
- bool _MMXPresent;
- bool _SSEPresent;
- bool _SSE2Present;
- bool _3DNowPresent;
- bool _3DNowExtPresent;
- CPU_TYPES _CPUType;
- Common::String _CPUVendorID;
- */
-
// Resourcemanager
// ---------------
- ResourceManager *_pResourceManager;
+ ResourceManager *_resourceManager;
- bool _RegisterScriptBindings();
-};
+ GraphicEngine *_gfx;
+ SoundEngine *_sfx;
+ InputEngine *_input;
+ PackageManager *_package;
+ ScriptEngine *_script;
+ Geometry *_geometry;
+ MoviePlayer *_fmv;
-/**
- * This is only a small class that manages the data of a service. It is a little ugly, I know,
- * but with Common::String a simple struct could not be used.
- */
-class BS_ServiceInfo {
-public:
- BS_ServiceInfo(const Common::String &SuperclassIdentifier_, const Common::String &ServiceIdentifier_,
- Service*(*CreateMethod_)(Kernel *)) {
- this->SuperclassIdentifier = SuperclassIdentifier_;
- this->ServiceIdentifier = ServiceIdentifier_;
- this->CreateMethod = CreateMethod_;
- };
-
- Common::String SuperclassIdentifier;
- Common::String ServiceIdentifier;
- Service*(*CreateMethod)(Kernel *);
+ bool registerScriptBindings();
};
-template<class T>
-void ReverseArray(Common::Array<T> &Arr) {
- if (Arr.size() < 2)
- return;
-
- for (uint i = 0; i <= (Arr.size() / 2 - 1); ++i) {
- T temp = Arr[i];
- Arr[i] = Arr[Arr.size() - i - 1];
- Arr[Arr.size() - i - 1] = temp;
- }
-}
-
} // End of namespace Sword25
#endif
diff --git a/engines/sword25/kernel/kernel_script.cpp b/engines/sword25/kernel/kernel_script.cpp
index 1b87dfdc6e..d4b9a56d8e 100644
--- a/engines/sword25/kernel/kernel_script.cpp
+++ b/engines/sword25/kernel/kernel_script.cpp
@@ -32,14 +32,9 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/kernel/kernel.h"
#include "sword25/kernel/filesystemutil.h"
-#include "sword25/kernel/window.h"
#include "sword25/kernel/resmanager.h"
#include "sword25/kernel/persistenceservice.h"
#include "sword25/script/script.h"
@@ -47,131 +42,92 @@
namespace Sword25 {
-// -----------------------------------------------------------------------------
-
-static int DisconnectService(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushboolean(L, pKernel->DisconnectService(luaL_checkstring(L, 1)));
+static int disconnectService(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushboolean(L, true);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetActiveServiceIdentifier(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushstring(L, pKernel->GetActiveServiceIdentifier(luaL_checkstring(L, 1)).c_str());
+static int getActiveServiceIdentifier(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushstring(L, "QUUX");
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSuperclassCount(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushnumber(L, pKernel->GetSuperclassCount());
+static int getSuperclassCount(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushnumber(L, 0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSuperclassIdentifier(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushstring(L, pKernel->GetSuperclassIdentifier(
- static_cast<uint>(luaL_checknumber(L, 1))).c_str());
+static int getSuperclassIdentifier(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushstring(L, "FOO");
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetServiceCount(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushnumber(L, pKernel->GetServiceCount(luaL_checkstring(L, 1)));
+static int getServiceCount(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushnumber(L, 0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetServiceIdentifier(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushstring(L, pKernel->GetServiceIdentifier(luaL_checkstring(L, 1),
- static_cast<uint>(luaL_checknumber(L, 2))).c_str());
+static int getServiceIdentifier(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushstring(L, "BAR");
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetMilliTicks(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int getMilliTicks(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- lua_pushnumber(L, pKernel->GetMilliTicks());
+ lua_pushnumber(L, pKernel->getMilliTicks());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetTimer(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int getTimer(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- lua_pushnumber(L, static_cast<lua_Number>(pKernel->GetMicroTicks()) / 1000000.0);
+ lua_pushnumber(L, static_cast<lua_Number>(pKernel->getMilliTicks()) / 1000.0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int StartService(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
-
- lua_pushbooleancpp(L, pKernel->NewService(luaL_checkstring(L, 1), luaL_checkstring(L, 2)) != NULL);
+static int startService(lua_State *L) {
+ // This function is used by system/boot.lua to init all services.
+ // However, we do nothing here, as we just hard code the init sequence.
+ lua_pushbooleancpp(L, true);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int Sleep(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int sleep(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- pKernel->Sleep(static_cast<uint>(luaL_checknumber(L, 1) * 1000));
+ pKernel->sleep(static_cast<uint>(luaL_checknumber(L, 1) * 1000));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int Crash(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int crash(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- pKernel->Crash();
+ pKernel->crash();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int ExecuteFile(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int executeFile(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ScriptEngine *pSE = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pSE = pKernel->getScript();
BS_ASSERT(pSE);
lua_pushbooleancpp(L, pSE->executeFile(luaL_checkstring(L, 1)));
@@ -179,551 +135,406 @@ static int ExecuteFile(lua_State *L) {
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int GetUserdataDirectory(lua_State *L) {
- lua_pushstring(L, FileSystemUtil::GetInstance().GetUserdataDirectory().c_str());
+static int getUserdataDirectory(lua_State *L) {
+ lua_pushstring(L, FileSystemUtil::getUserdataDirectory().c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetPathSeparator(lua_State *L) {
- lua_pushstring(L, FileSystemUtil::GetInstance().GetPathSeparator().c_str());
+static int getPathSeparator(lua_State *L) {
+ lua_pushstring(L, FileSystemUtil::getPathSeparator().c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int FileExists(lua_State *L) {
- lua_pushbooleancpp(L, FileSystemUtil::GetInstance().FileExists(luaL_checkstring(L, 1)));
+static int fileExists(lua_State *L) {
+ lua_pushbooleancpp(L, FileSystemUtil::fileExists(luaL_checkstring(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int CreateDirectory(lua_State *L) {
- lua_pushbooleancpp(L, FileSystemUtil::GetInstance().CreateDirectory(luaL_checkstring(L, 1)));
+static int createDirectory(lua_State *L) {
+ // ScummVM engines cannot create directories, so we do nothing here.
+ lua_pushbooleancpp(L, false);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetWinCode(lua_State *L) {
+static int getWinCode(lua_State *L) {
lua_pushstring(L, "ScummVM");
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSubversionRevision(lua_State *L) {
+static int getSubversionRevision(lua_State *L) {
// ScummVM is 1337
lua_pushnumber(L, 1337);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetUsedMemory(lua_State *L) {
- lua_pushnumber(L, Kernel::GetInstance()->GetUsedMemory());
+static int getUsedMemory(lua_State *L) {
+ lua_pushnumber(L, Kernel::getInstance()->getUsedMemory());
return 1;
}
-// -----------------------------------------------------------------------------
-
static const char *KERNEL_LIBRARY_NAME = "Kernel";
static const luaL_reg KERNEL_FUNCTIONS[] = {
- {"DisconnectService", DisconnectService},
- {"GetActiveServiceIdentifier", GetActiveServiceIdentifier},
- {"GetSuperclassCount", GetSuperclassCount},
- {"GetSuperclassIdentifier", GetSuperclassIdentifier},
- {"GetServiceCount", GetServiceCount},
- {"GetServiceIdentifier", GetServiceIdentifier},
- {"GetMilliTicks", GetMilliTicks},
- {"GetTimer", GetTimer},
- {"StartService", StartService},
- {"Sleep", Sleep},
- {"Crash", Crash},
- {"ExecuteFile", ExecuteFile},
- {"GetUserdataDirectory", GetUserdataDirectory},
- {"GetPathSeparator", GetPathSeparator},
- {"FileExists", FileExists},
- {"CreateDirectory", CreateDirectory},
- {"GetWinCode", GetWinCode},
- {"GetSubversionRevision", GetSubversionRevision},
- {"GetUsedMemory", GetUsedMemory},
+ {"DisconnectService", disconnectService},
+ {"GetActiveServiceIdentifier", getActiveServiceIdentifier},
+ {"GetSuperclassCount", getSuperclassCount},
+ {"GetSuperclassIdentifier", getSuperclassIdentifier},
+ {"GetServiceCount", getServiceCount},
+ {"GetServiceIdentifier", getServiceIdentifier},
+ {"GetMilliTicks", getMilliTicks},
+ {"GetTimer", getTimer},
+ {"StartService", startService},
+ {"Sleep", sleep},
+ {"Crash", crash},
+ {"ExecuteFile", executeFile},
+ {"GetUserdataDirectory", getUserdataDirectory},
+ {"GetPathSeparator", getPathSeparator},
+ {"FileExists", fileExists},
+ {"CreateDirectory", createDirectory},
+ {"GetWinCode", getWinCode},
+ {"GetSubversionRevision", getSubversionRevision},
+ {"GetUsedMemory", getUsedMemory},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static int IsVisible(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushbooleancpp(L, pWindow->IsVisible());
+static int isVisible(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushbooleancpp(L, true);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetVisible(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- pWindow->SetVisible(lua_tobooleancpp(L, 1));
+static int setVisible(lua_State *L) {
+ // This function apparently is not used by the game scripts
+// pWindow->setVisible(lua_tobooleancpp(L, 1));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int GetX(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushnumber(L, pWindow->GetX());
+static int getX(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetY(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushnumber(L, pWindow->GetY());
+static int getY(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetX(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- pWindow->SetX(static_cast<int>(luaL_checknumber(L, 1)));
+static int setX(lua_State *L) {
+ // This is called by system/boot.lua with -1 as value.
+// pWindow->setX(static_cast<int>(luaL_checknumber(L, 1)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int SetY(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- pWindow->SetY(static_cast<int>(luaL_checknumber(L, 1)));
+static int setY(lua_State *L) {
+ // This is called by system/boot.lua with -1 as value.
+// pWindow->setY(static_cast<int>(luaL_checknumber(L, 1)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int GetClientX(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushnumber(L, pWindow->GetClientX());
+static int getClientX(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetClientY(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushnumber(L, pWindow->GetClientY());
+static int getClientY(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetWidth(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushnumber(L, pWindow->GetWidth());
+static int getWidth(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 800);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetHeight(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushnumber(L, pWindow->GetHeight());
+static int getHeight(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 600);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetWidth(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- pWindow->SetWidth(static_cast<int>(luaL_checknumber(L, 1)));
+static int setWidth(lua_State *L) {
+ // This is called by system/boot.lua with 800 as value.
+// pWindow->setWidth(static_cast<int>(luaL_checknumber(L, 1)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int SetHeight(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- pWindow->SetHeight(static_cast<int>(luaL_checknumber(L, 1)));
+static int setHeight(lua_State *L) {
+ // This is called by system/boot.lua with 600 as value.
+// pWindow->setHeight(static_cast<int>(luaL_checknumber(L, 1)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int GetTitle(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushstring(L, pWindow->GetTitle().c_str());
+static int getTitle(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushstring(L, "");
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetTitle(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- pWindow->SetTitle(luaL_checkstring(L, 1));
+static int setTitle(lua_State *L) {
+ // This is called by system/boot.lua and system/menu.lua, to
+ // set the window title to the (localized) game name.
+ // FIXME: Should we call OSystem::setWindowCaption() here?
+// pWindow->setTitle(luaL_checkstring(L, 1));
return 0;
}
-// -----------------------------------------------------------------------------
+static int processMessages(lua_State *L) {
+ // This is called by the main loop in system/boot.lua,
+ // and the game keeps running as true is returned here.
+ // It terminates if we return false.
-static int ProcessMessages(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
+ // TODO: We could do more stuff here if desired...
+
+ // TODO: We could always return true here, and leave quit handling
+ // to the closeWanted() opcode; see also the TODO comment in there.
- lua_pushbooleancpp(L, pWindow->ProcessMessages());
+ lua_pushbooleancpp(L, !Engine::shouldQuit());
return 1;
}
-// -----------------------------------------------------------------------------
+static int closeWanted(lua_State *L) {
+ // This is called by system/interface.lua to determine whether the
+ // user requested the game to close (e.g. by clicking the 'close' widget
+ // of the game window). As a consequence (i.e. this function returns true),
+ // a quit confirmation dialog is shown.
-static int CloseWanted(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushbooleancpp(L, pWindow->CloseWanted());
+ // TODO: ScummVM currently has a bug / misfeature where some engines provide
+ // quit confirmation dialogs, some don't; in addition, we have a global confirmation
+ // dialog (but the user has to explicitly activate that in the config).
+ // Anyway, this can lead to *two* confirmation dialogs being shown.
+ // If it wasn't for that, we could simply check for Engine::shouldQuit() here,
+ // and then invoke EventMan::resetQuit. But currently this would result in
+ // the user seeing two confirmation dialogs. Bad.
+ lua_pushbooleancpp(L, false);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int WaitForFocus(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushbooleancpp(L, pWindow->WaitForFocus());
+static int waitForFocus(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushbooleancpp(L, true);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int HasFocus(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
- BS_ASSERT(pKernel);
- Window *pWindow = pKernel->GetWindow();
- BS_ASSERT(pWindow);
-
- lua_pushbooleancpp(L, pWindow->HasFocus());
+static int hasFocus(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushbooleancpp(L, true);
return 1;
}
-// -----------------------------------------------------------------------------
-
static const char *WINDOW_LIBRARY_NAME = "Window";
static const luaL_reg WINDOW_FUNCTIONS[] = {
- {"IsVisible", IsVisible},
- {"SetVisible", SetVisible},
- {"GetX", GetX},
- {"SetX", SetX},
- {"GetY", GetY},
- {"SetY", SetY},
- {"GetClientX", GetClientX},
- {"GetClientY", GetClientY},
- {"GetWidth", GetWidth},
- {"GetHeight", GetHeight},
- {"SetWidth", SetWidth},
- {"SetHeight", SetHeight},
- {"GetTitle", GetTitle},
- {"SetTitle", SetTitle},
- {"ProcessMessages", ProcessMessages},
- {"CloseWanted", CloseWanted},
- {"WaitForFocus", WaitForFocus},
- {"HasFocus", HasFocus},
+ {"IsVisible", isVisible},
+ {"SetVisible", setVisible},
+ {"GetX", getX},
+ {"SetX", setX},
+ {"GetY", getY},
+ {"SetY", setY},
+ {"GetClientX", getClientX},
+ {"GetClientY", getClientY},
+ {"GetWidth", getWidth},
+ {"GetHeight", getHeight},
+ {"SetWidth", setWidth},
+ {"SetHeight", setHeight},
+ {"GetTitle", getTitle},
+ {"SetTitle", setTitle},
+ {"ProcessMessages", processMessages},
+ {"CloseWanted", closeWanted},
+ {"WaitForFocus", waitForFocus},
+ {"HasFocus", hasFocus},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static int PrecacheResource(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int precacheResource(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1)));
+ lua_pushbooleancpp(L, pResource->precacheResource(luaL_checkstring(L, 1)));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int ForcePrecacheResource(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int forcePrecacheResource(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1), true));
+ lua_pushbooleancpp(L, pResource->precacheResource(luaL_checkstring(L, 1), true));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetMaxMemoryUsage(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int getMaxMemoryUsage(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- lua_pushnumber(L, pResource->GetMaxMemoryUsage());
+ lua_pushnumber(L, pResource->getMaxMemoryUsage());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetMaxMemoryUsage(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int setMaxMemoryUsage(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- pResource->SetMaxMemoryUsage(static_cast<uint>(lua_tonumber(L, 1)));
+ pResource->setMaxMemoryUsage(static_cast<uint>(lua_tonumber(L, 1)));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int EmptyCache(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int emptyCache(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- pResource->EmptyCache();
+ pResource->emptyCache();
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int IsLogCacheMiss(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int isLogCacheMiss(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- lua_pushbooleancpp(L, pResource->IsLogCacheMiss());
+ lua_pushbooleancpp(L, pResource->isLogCacheMiss());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SetLogCacheMiss(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int setLogCacheMiss(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- pResource->SetLogCacheMiss(lua_tobooleancpp(L, 1));
+ pResource->setLogCacheMiss(lua_tobooleancpp(L, 1));
return 0;
}
-// -----------------------------------------------------------------------------
-
-static int DumpLockedResources(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+static int dumpLockedResources(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ResourceManager *pResource = pKernel->GetResourceManager();
+ ResourceManager *pResource = pKernel->getResourceManager();
BS_ASSERT(pResource);
- pResource->DumpLockedResources();
+ pResource->dumpLockedResources();
return 0;
}
-// -----------------------------------------------------------------------------
-
static const char *RESOURCE_LIBRARY_NAME = "Resource";
static const luaL_reg RESOURCE_FUNCTIONS[] = {
- {"PrecacheResource", PrecacheResource},
- {"ForcePrecacheResource", ForcePrecacheResource},
- {"GetMaxMemoryUsage", GetMaxMemoryUsage},
- {"SetMaxMemoryUsage", SetMaxMemoryUsage},
- {"EmptyCache", EmptyCache},
- {"IsLogCacheMiss", IsLogCacheMiss},
- {"SetLogCacheMiss", SetLogCacheMiss},
- {"DumpLockedResources", DumpLockedResources},
+ {"PrecacheResource", precacheResource},
+ {"ForcePrecacheResource", forcePrecacheResource},
+ {"GetMaxMemoryUsage", getMaxMemoryUsage},
+ {"SetMaxMemoryUsage", setMaxMemoryUsage},
+ {"EmptyCache", emptyCache},
+ {"IsLogCacheMiss", isLogCacheMiss},
+ {"SetLogCacheMiss", setLogCacheMiss},
+ {"DumpLockedResources", dumpLockedResources},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-static int ReloadSlots(lua_State *L) {
- PersistenceService::GetInstance().ReloadSlots();
+static int reloadSlots(lua_State *L) {
+ PersistenceService::getInstance().reloadSlots();
lua_pushnil(L);
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSlotCount(lua_State *L) {
- lua_pushnumber(L, PersistenceService::GetInstance().GetSlotCount());
+static int getSlotCount(lua_State *L) {
+ lua_pushnumber(L, PersistenceService::getInstance().getSlotCount());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int IsSlotOccupied(lua_State *L) {
- lua_pushbooleancpp(L, PersistenceService::GetInstance().IsSlotOccupied(
- static_cast<uint>(luaL_checknumber(L, 1)) - 1));
+static int isSlotOccupied(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::getInstance().isSlotOccupied(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSavegameDirectory(lua_State *L) {
- lua_pushstring(L, PersistenceService::GetInstance().GetSavegameDirectory().c_str());
+static int getSavegameDirectory(lua_State *L) {
+ lua_pushstring(L, PersistenceService::getInstance().getSavegameDirectory().c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int IsSavegameCompatible(lua_State *L) {
- lua_pushbooleancpp(L, PersistenceService::GetInstance().IsSavegameCompatible(
+static int isSavegameCompatible(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::getInstance().isSavegameCompatible(
static_cast<uint>(luaL_checknumber(L, 1)) - 1));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSavegameDescription(lua_State *L) {
- lua_pushstring(L, PersistenceService::GetInstance().GetSavegameDescription(
+static int getSavegameDescription(lua_State *L) {
+ lua_pushstring(L, PersistenceService::getInstance().getSavegameDescription(
static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int GetSavegameFilename(lua_State *L) {
- lua_pushstring(L, PersistenceService::GetInstance().GetSavegameFilename(static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
+static int getSavegameFilename(lua_State *L) {
+ lua_pushstring(L, PersistenceService::getInstance().getSavegameFilename(static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int LoadGame(lua_State *L) {
- lua_pushbooleancpp(L, PersistenceService::GetInstance().LoadGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
+static int loadGame(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::getInstance().loadGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
return 1;
}
-// -----------------------------------------------------------------------------
-
-static int SaveGame(lua_State *L) {
- lua_pushbooleancpp(L, PersistenceService::GetInstance().SaveGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2)));
+static int saveGame(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::getInstance().saveGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2)));
return 1;
}
-// -----------------------------------------------------------------------------
-
static const char *PERSISTENCE_LIBRARY_NAME = "Persistence";
static const luaL_reg PERSISTENCE_FUNCTIONS[] = {
- {"ReloadSlots", ReloadSlots},
- {"GetSlotCount", GetSlotCount},
- {"IsSlotOccupied", IsSlotOccupied},
- {"GetSavegameDirectory", GetSavegameDirectory},
- {"IsSavegameCompatible", IsSavegameCompatible},
- {"GetSavegameDescription", GetSavegameDescription},
- {"GetSavegameFilename", GetSavegameFilename},
- {"LoadGame", LoadGame},
- {"SaveGame", SaveGame},
+ {"ReloadSlots", reloadSlots},
+ {"GetSlotCount", getSlotCount},
+ {"IsSlotOccupied", isSlotOccupied},
+ {"GetSavegameDirectory", getSavegameDirectory},
+ {"IsSavegameCompatible", isSavegameCompatible},
+ {"GetSavegameDescription", getSavegameDescription},
+ {"GetSavegameFilename", getSavegameFilename},
+ {"LoadGame", loadGame},
+ {"SaveGame", saveGame},
{0, 0}
};
-// -----------------------------------------------------------------------------
-
-bool Kernel::_RegisterScriptBindings() {
- ScriptEngine *pScript = static_cast<ScriptEngine *>(GetService("script"));
+bool Kernel::registerScriptBindings() {
+ ScriptEngine *pScript = getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
diff --git a/engines/sword25/kernel/log.cpp b/engines/sword25/kernel/log.cpp
index 259c02449f..91b0c36ac1 100644
--- a/engines/sword25/kernel/log.cpp
+++ b/engines/sword25/kernel/log.cpp
@@ -39,33 +39,31 @@
namespace Sword25 {
-// Constants
static const char *BF_LOG_FILENAME = "log.txt";
static const size_t LOG_BUFFERSIZE = 1024 * 16;
// Logging will take place only when it's activated
#ifdef BS_ACTIVATE_LOGGING
-Common::WriteStream *BS_Log::_LogFile = NULL;
-bool BS_Log::_LineBegin = true;
-const char *BS_Log::_Prefix = NULL;
-const char *BS_Log::_File = NULL;
-int BS_Log::_Line = 0;
-bool BS_Log::_AutoNewline = false;
-Common::Array<BS_Log::LOG_LISTENER_CALLBACK> BS_Log::_LogListener;
+Common::WriteStream *BS_Log::_logFile = NULL;
+bool BS_Log::_lineBegin = true;
+const char *BS_Log::_prefix = NULL;
+const char *BS_Log::_file = NULL;
+int BS_Log::_line = 0;
+bool BS_Log::_autoNewline = false;
-bool BS_Log::_CreateLog() {
+bool BS_Log::createLog() {
// Open the log file
Common::FSNode dataDir(ConfMan.get("path"));
Common::FSNode file = dataDir.getChild(BF_LOG_FILENAME);
// Open the file for saving
- _LogFile = file.createWriteStream();
+ _logFile = file.createWriteStream();
- if (_LogFile) {
+ if (_logFile) {
// Add a title into the log file
- Log("Broken Sword 2.5 Engine - Build: %s - %s - VersionID: %s\n", __DATE__, __TIME__, gScummVMFullVersion);
- Log("-----------------------------------------------------------------------------------------------------\n");
+ log("Broken Sword 2.5 Engine - Build: %s - %s - VersionID: %s\n", __DATE__, __TIME__, gScummVMFullVersion);
+ log("-----------------------------------------------------------------------------------------------------\n");
return true;
}
@@ -74,140 +72,142 @@ bool BS_Log::_CreateLog() {
return false;
}
-void BS_Log::_CloseLog() {
- delete _LogFile;
- _LogFile = NULL;
+void BS_Log::closeLog() {
+ delete _logFile;
+ _logFile = NULL;
}
-void BS_Log::Log(const char *Format, ...) {
- char Message[LOG_BUFFERSIZE];
+void BS_Log::log(const char *format, ...) {
+ char message[LOG_BUFFERSIZE];
// Create the message
- va_list ArgList;
- va_start(ArgList, Format);
- vsnprintf(Message, sizeof(Message), Format, ArgList);
+ va_list argList;
+ va_start(argList, format);
+ vsnprintf(message, sizeof(message), format, argList);
// Log the message
- _WriteLog(Message);
+ writeLog(message);
- _FlushLog();
+ flushLog();
}
-void BS_Log::LogPrefix(const char *Prefix, const char *Format, ...) {
- char Message[LOG_BUFFERSIZE];
- char ExtFormat[LOG_BUFFERSIZE];
+void BS_Log::logPrefix(const char *prefix, const char *format, ...) {
+ char message[LOG_BUFFERSIZE];
+ char extFormat[LOG_BUFFERSIZE];
// If the issue has ceased at the beginning of a new line, the new issue to begin with the prefix
- ExtFormat[0] = 0;
- if (_LineBegin) {
- snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix);
- _LineBegin = false;
+ extFormat[0] = 0;
+ if (_lineBegin) {
+ snprintf(extFormat, sizeof(extFormat), "%s: ", prefix);
+ _lineBegin = false;
}
// Format String pass line by line and each line with the initial prefix
for (;;) {
- const char *NextLine = strstr(Format, "\n");
- if (!NextLine || *(NextLine + strlen("\n")) == 0) {
- snprintf(ExtFormat, sizeof(ExtFormat), "%s%s", ExtFormat, Format);
- if (NextLine) _LineBegin = true;
+ const char *nextLine = strstr(format, "\n");
+ if (!nextLine || *(nextLine + strlen("\n")) == 0) {
+ Common::strlcat(extFormat, format, sizeof(extFormat));
+ if (nextLine)
+ _lineBegin = true;
break;
} else {
- strncat(ExtFormat, Format, (NextLine - Format) + strlen("\n"));
- snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix);
+ strncat(extFormat, format, (nextLine - format) + strlen("\n"));
+ Common::strlcat(extFormat, prefix, sizeof(extFormat));
+ Common::strlcat(extFormat, ": ", sizeof(extFormat));
}
- Format = NextLine + strlen("\n");
+ format = nextLine + strlen("\n");
}
// Create message
- va_list ArgList;
- va_start(ArgList, Format);
- vsnprintf(Message, sizeof(Message), ExtFormat, ArgList);
+ va_list argList;
+ va_start(argList, format);
+ vsnprintf(message, sizeof(message), extFormat, argList);
// Log the message
- _WriteLog(Message);
+ writeLog(message);
- _FlushLog();
+ flushLog();
}
-void BS_Log::LogDecorated(const char *Format, ...) {
+void BS_Log::logDecorated(const char *format, ...) {
// Nachricht erzeugen
- char Message[LOG_BUFFERSIZE];
- va_list ArgList;
- va_start(ArgList, Format);
- vsnprintf(Message, sizeof(Message), Format, ArgList);
+ char message[LOG_BUFFERSIZE];
+ va_list argList;
+ va_start(argList, format);
+ vsnprintf(message, sizeof(message), format, argList);
- // Zweiten Prefix erzeugen, falls gewünscht
- char SecondaryPrefix[1024];
- if (_File && _Line)
- snprintf(SecondaryPrefix, sizeof(SecondaryPrefix), "(file: %s, line: %d) - ", _File, _Line);
+ // Zweiten prefix erzeugen, falls gewünscht
+ char secondaryPrefix[1024];
+ if (_file && _line)
+ snprintf(secondaryPrefix, sizeof(secondaryPrefix), "(file: %s, line: %d) - ", _file, _line);
// Nachricht zeilenweise ausgeben und an jeden Zeilenanfang das Präfix setzen
- char *MessageWalker = Message;
+ char *messageWalker = message;
for (;;) {
- char *NextLine = strstr(MessageWalker, "\n");
- if (NextLine) {
- *NextLine = 0;
- if (_LineBegin) {
- _WriteLog(_Prefix);
- if (_File && _Line)
- _WriteLog(SecondaryPrefix);
+ char *nextLine = strstr(messageWalker, "\n");
+ if (nextLine) {
+ *nextLine = 0;
+ if (_lineBegin) {
+ writeLog(_prefix);
+ if (_file && _line)
+ writeLog(secondaryPrefix);
}
- _WriteLog(MessageWalker);
- _WriteLog("\n");
- MessageWalker = NextLine + sizeof("\n") - 1;
- _LineBegin = true;
+ writeLog(messageWalker);
+ writeLog("\n");
+ messageWalker = nextLine + sizeof("\n") - 1;
+ _lineBegin = true;
} else {
- if (_LineBegin) {
- _WriteLog(_Prefix);
- if (_File && _Line)
- _WriteLog(SecondaryPrefix);
+ if (_lineBegin) {
+ writeLog(_prefix);
+ if (_file && _line)
+ writeLog(secondaryPrefix);
}
- _WriteLog(MessageWalker);
- _LineBegin = false;
+ writeLog(messageWalker);
+ _lineBegin = false;
break;
}
}
// Falls gewünscht, wird ans Ende der Nachricht automatisch ein Newline angehängt.
- if (_AutoNewline) {
- _WriteLog("\n");
- _LineBegin = true;
+ if (_autoNewline) {
+ writeLog("\n");
+ _lineBegin = true;
}
// Pseudoparameter zurücksetzen
- _Prefix = NULL;
- _File = 0;
- _Line = 0;
- _AutoNewline = false;
+ _prefix = NULL;
+ _file = 0;
+ _line = 0;
+ _autoNewline = false;
- _FlushLog();
+ flushLog();
}
-int BS_Log::_WriteLog(const char *Message) {
- if (!_LogFile) if (!_CreateLog()) return false;
+int BS_Log::writeLog(const char *message) {
+ if (!_logFile && !createLog())
+ return false;
- Common::Array<LOG_LISTENER_CALLBACK>::iterator Iter = _LogListener.begin();
- for (; Iter != _LogListener.end(); ++Iter)
- (*Iter)(Message);
+ debugN(0, "%s", message);
- _LogFile->writeString(Message);
+ _logFile->writeString(message);
return true;
}
-void BS_Log::_FlushLog() {
- _LogFile->flush();
+void BS_Log::flushLog() {
+ if (_logFile)
+ _logFile->flush();
}
-void (*BS_LogPtr)(const char *, ...) = BS_Log::Log;
+void (*BS_LogPtr)(const char *, ...) = BS_Log::log;
-void BS_Log_C(const char *Message) {
- BS_LogPtr(Message);
+void BS_Log_C(const char *message) {
+ BS_LogPtr(message);
}
#else
-void BS_Log_C(const char *Message) {};
+void BS_Log_C(const char *message) {};
#endif
diff --git a/engines/sword25/kernel/log.h b/engines/sword25/kernel/log.h
index 1fe9ff4ed6..e7c5789a53 100644
--- a/engines/sword25/kernel/log.h
+++ b/engines/sword25/kernel/log.h
@@ -46,63 +46,50 @@ namespace Sword25 {
#ifdef BS_ACTIVATE_LOGGING
// Logging-Makros
-#define BS_LOG BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::LogDecorated
-#define BS_LOGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
-#define BS_LOG_WARNING BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::LogDecorated
-#define BS_LOG_WARNINGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
-#define BS_LOG_ERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::LogDecorated
-#define BS_LOG_ERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
-#define BS_LOG_EXTERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::LogDecorated
-#define BS_LOG_EXTERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG BS_Log::setPrefix(BS_LOG_PREFIX ": "), BS_Log::logDecorated
+#define BS_LOGLN BS_Log::setPrefix(BS_LOG_PREFIX ": "), BS_Log::setAutoNewline(true), BS_Log::logDecorated
+#define BS_LOG_WARNING BS_Log::setPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::logDecorated
+#define BS_LOG_WARNINGLN BS_Log::setPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::setAutoNewline(true), BS_Log::logDecorated
+#define BS_LOG_ERROR BS_Log::setPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::logDecorated
+#define BS_LOG_ERRORLN BS_Log::setPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::setAutoNewline(true), BS_Log::logDecorated
+#define BS_LOG_EXTERROR BS_Log::setPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::setFile(__FILE__), BS_Log::setLine(__LINE__), BS_Log::logDecorated
+#define BS_LOG_EXTERRORLN BS_Log::setPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::setFile(__FILE__), BS_Log::setLine(__LINE__), BS_Log::setAutoNewline(true), BS_Log::logDecorated
// Die Version der Logging-Klasse mit aktiviertem Logging
class BS_Log {
public:
- static void Clear();
- static void Log(const char *Format, ...);
- static void LogPrefix(const char *Prefix, const char *Format, ...);
- static void LogDecorated(const char *Format, ...);
+ static void clear();
+ static void log(const char *format, ...);
+ static void logPrefix(const char *prefix, const char *format, ...);
+ static void logDecorated(const char *format, ...);
- static void SetPrefix(const char *Prefix) {
- _Prefix = Prefix;
+ static void setPrefix(const char *prefix) {
+ _prefix = prefix;
}
- static void SetFile(const char *File) {
- _File = File;
+ static void setFile(const char *file) {
+ _file = file;
}
- static void SetLine(int Line) {
- _Line = Line;
+ static void setLine(int line) {
+ _line = line;
}
- static void SetAutoNewline(bool AutoNewline) {
- _AutoNewline = AutoNewline;
+ static void setAutoNewline(bool autoNewline) {
+ _autoNewline = autoNewline;
}
- typedef void (*LOG_LISTENER_CALLBACK)(const char *);
- static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) {
- _LogListener.push_back(Callback);
- }
- static bool IsListenerRegistered(LOG_LISTENER_CALLBACK Callback) {
- Common::Array<LOG_LISTENER_CALLBACK>::iterator i;
- for (i = _LogListener.begin(); i != _LogListener.end(); ++i) {
- if (**i == Callback)
- return true;
- }
- return false;
- }
- static void _CloseLog();
+ static void closeLog();
private:
- static Common::WriteStream *_LogFile;
- static bool _LineBegin;
- static const char *_Prefix;
- static const char *_File;
- static int _Line;
- static bool _AutoNewline;
- static Common::Array<LOG_LISTENER_CALLBACK> _LogListener;
-
- static bool _CreateLog();
-
- static int _WriteLog(const char *Message);
- static void _FlushLog();
+ static Common::WriteStream *_logFile;
+ static bool _lineBegin;
+ static const char *_prefix;
+ static const char *_file;
+ static int _line;
+ static bool _autoNewline;
+
+ static bool createLog();
+
+ static int writeLog(const char *message);
+ static void flushLog();
};
// Auxiliary function that allows to log C functions (needed for Lua).
@@ -125,17 +112,17 @@ private:
class BS_Log {
public:
// This version implements all the various methods as empty stubs
- static void Log(const char *Text, ...) {};
- static void LogPrefix(const char *Prefix, const char *Format, ...) {};
- static void LogDecorated(const char *Format, ...) {};
+ static void log(const char *text, ...) {};
+ static void logPrefix(const char *prefix, const char *format, ...) {};
+ static void logDecorated(const char *format, ...) {};
- static void SetPrefix(const char *Prefix) {};
- static void SetFile(const char *File) {};
- static void SetLine(int Line) {};
- static void SetAutoNewline(bool AutoNewline) {};
+ static void setPrefix(const char *prefix) {};
+ static void setFile(const char *file) {};
+ static void setLine(int line) {};
+ static void setAutoNewline(bool autoNewline) {};
typedef void (*LOG_LISTENER_CALLBACK)(const char *);
- static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) {};
+ static void registerLogListener(LOG_LISTENER_CALLBACK callback) {};
};
#define BS_Log_C error
diff --git a/engines/sword25/kernel/objectregistry.h b/engines/sword25/kernel/objectregistry.h
index dc702f2d75..3a27ba4942 100644
--- a/engines/sword25/kernel/objectregistry.h
+++ b/engines/sword25/kernel/objectregistry.h
@@ -37,7 +37,6 @@
#include "common/func.h"
#include "common/hashmap.h"
-#include "sword25/kernel/bs_stdint.h"
#include "sword25/kernel/common.h"
namespace Sword25 {
@@ -131,7 +130,6 @@ public:
}
protected:
- // FIXME: I'm not entirely sure my current hash function is legitimate
struct ClassPointer_EqualTo {
bool operator()(const T *x, const T *y) const {
return x == y;
@@ -139,7 +137,7 @@ protected:
};
struct ClassPointer_Hash {
uint operator()(const T *x) const {
- return static_cast<uint>((int64)x % ((int64)1 << sizeof(uint)));
+ return *(uint *)&x;
}
};
diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp
index 438fa7b222..b1c3233b59 100644
--- a/engines/sword25/kernel/outputpersistenceblock.cpp
+++ b/engines/sword25/kernel/outputpersistenceblock.cpp
@@ -34,97 +34,67 @@
#define BS_LOG_PREFIX "OUTPUTPERSISTENCEBLOCK"
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/outputpersistenceblock.h"
-// -----------------------------------------------------------------------------
-// Constants
-// -----------------------------------------------------------------------------
-
namespace {
const uint INITIAL_BUFFER_SIZE = 1024 * 64;
}
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Construction / Destruction
-// -----------------------------------------------------------------------------
-
OutputPersistenceBlock::OutputPersistenceBlock() {
- m_Data.reserve(INITIAL_BUFFER_SIZE);
+ _data.reserve(INITIAL_BUFFER_SIZE);
}
-// -----------------------------------------------------------------------------
-// Writing
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::write(signed int Value) {
- WriteMarker(SINT_MARKER);
- Value = ConvertEndianessFromSystemToStorage(Value);
- RawWrite(&Value, sizeof(Value));
+void OutputPersistenceBlock::write(signed int value) {
+ writeMarker(SINT_MARKER);
+ value = convertEndianessFromSystemToStorage(value);
+ rawWrite(&value, sizeof(value));
}
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::write(uint Value) {
- WriteMarker(UINT_MARKER);
- Value = ConvertEndianessFromSystemToStorage(Value);
- RawWrite(&Value, sizeof(Value));
+void OutputPersistenceBlock::write(uint value) {
+ writeMarker(UINT_MARKER);
+ value = convertEndianessFromSystemToStorage(value);
+ rawWrite(&value, sizeof(value));
}
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::write(float Value) {
- WriteMarker(FLOAT_MARKER);
- Value = ConvertEndianessFromSystemToStorage(Value);
- RawWrite(&Value, sizeof(Value));
+void OutputPersistenceBlock::write(float value) {
+ writeMarker(FLOAT_MARKER);
+ value = convertEndianessFromSystemToStorage(value);
+ rawWrite(&value, sizeof(value));
}
-// -----------------------------------------------------------------------------
+void OutputPersistenceBlock::write(bool value) {
+ writeMarker(BOOL_MARKER);
-void OutputPersistenceBlock::write(bool Value) {
- WriteMarker(BOOL_MARKER);
-
- uint UIntBool = Value ? 1 : 0;
- UIntBool = ConvertEndianessFromSystemToStorage(UIntBool);
- RawWrite(&UIntBool, sizeof(UIntBool));
+ uint uintBool = value ? 1 : 0;
+ uintBool = convertEndianessFromSystemToStorage(uintBool);
+ rawWrite(&uintBool, sizeof(uintBool));
}
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::write(const Common::String &String) {
- WriteMarker(STRING_MARKER);
+void OutputPersistenceBlock::writeString(const Common::String &string) {
+ writeMarker(STRING_MARKER);
- write(String.size());
- RawWrite(String.c_str(), String.size());
+ write(string.size());
+ rawWrite(string.c_str(), string.size());
}
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::write(const void *BufferPtr, size_t Size) {
- WriteMarker(BLOCK_MARKER);
+void OutputPersistenceBlock::writeByteArray(Common::Array<byte> &value) {
+ writeMarker(BLOCK_MARKER);
- write(Size);
- RawWrite(BufferPtr, Size);
+ write((uint)value.size());
+ rawWrite(&value[0], value.size());
}
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::WriteMarker(byte Marker) {
- m_Data.push_back(Marker);
+void OutputPersistenceBlock::writeMarker(byte marker) {
+ _data.push_back(marker);
}
-// -----------------------------------------------------------------------------
-
-void OutputPersistenceBlock::RawWrite(const void *DataPtr, size_t Size) {
- if (Size > 0) {
- uint OldSize = m_Data.size();
- m_Data.resize(OldSize + Size);
- memcpy(&m_Data[OldSize], DataPtr, Size);
+void OutputPersistenceBlock::rawWrite(const void *dataPtr, size_t size) {
+ if (size > 0) {
+ uint oldSize = _data.size();
+ _data.resize(oldSize + size);
+ memcpy(&_data[oldSize], dataPtr, size);
}
}
diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h
index 154dbc9763..71dbe68a52 100644
--- a/engines/sword25/kernel/outputpersistenceblock.h
+++ b/engines/sword25/kernel/outputpersistenceblock.h
@@ -35,42 +35,34 @@
#ifndef SWORD25_OUTPUTPERSISTENCEBLOCK_H
#define SWORD25_OUTPUTPERSISTENCEBLOCK_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
#include "sword25/kernel/persistenceblock.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Class declaration
-// -----------------------------------------------------------------------------
-
class OutputPersistenceBlock : public PersistenceBlock {
public:
OutputPersistenceBlock();
- void write(signed int Value);
- void write(uint Value);
- void write(float Value);
- void write(bool Value);
- void write(const Common::String &String);
- void write(const void *BufferPtr, size_t Size);
+ void write(signed int value);
+ void write(uint value);
+ void write(float value);
+ void write(bool value);
+ void writeString(const Common::String &string);
+ void writeByteArray(Common::Array<byte> &value);
- const void *GetData() const {
- return &m_Data[0];
+ const void *getData() const {
+ return &_data[0];
}
- uint GetDataSize() const {
- return m_Data.size();
+ uint getDataSize() const {
+ return _data.size();
}
private:
- void WriteMarker(byte Marker);
- void RawWrite(const void *DataPtr, size_t Size);
+ void writeMarker(byte marker);
+ void rawWrite(const void *dataPtr, size_t size);
- Common::Array<byte> m_Data;
+ Common::Array<byte> _data;
};
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/persistable.h b/engines/sword25/kernel/persistable.h
index fc314688d5..25cf70fda0 100644
--- a/engines/sword25/kernel/persistable.h
+++ b/engines/sword25/kernel/persistable.h
@@ -42,7 +42,7 @@ class InputPersistenceBlock;
class Persistable {
public:
- virtual ~Persistable() {};
+ virtual ~Persistable() {}
virtual bool persist(OutputPersistenceBlock &writer) = 0;
virtual bool unpersist(InputPersistenceBlock &reader) = 0;
diff --git a/engines/sword25/kernel/persistenceblock.h b/engines/sword25/kernel/persistenceblock.h
index 1f043aa68a..4a64eff11b 100644
--- a/engines/sword25/kernel/persistenceblock.h
+++ b/engines/sword25/kernel/persistenceblock.h
@@ -35,34 +35,26 @@
#ifndef SWORD25_PERSISTENCEBLOCK_H
#define SWORD25_PERSISTENCEBLOCK_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Class definition
-// -----------------------------------------------------------------------------
-
class PersistenceBlock {
public:
- static uint GetSInt32Size() {
+ static uint getSInt32Size() {
return sizeof(signed int) + sizeof(byte);
}
- static uint GetUInt32Size() {
+ static uint getUInt32Size() {
return sizeof(uint) + sizeof(byte);
}
- static uint GetFloat32Size() {
+ static uint getFloat32Size() {
return sizeof(float) + sizeof(byte);
}
- static uint GetBoolSize() {
+ static uint getBoolSize() {
return sizeof(byte) + sizeof(byte);
}
- static uint GetStringSize(const Common::String &String) {
- return static_cast<uint>(sizeof(uint) + String.size() + sizeof(byte));
+ static uint getStringSize(const Common::String &string) {
+ return static_cast<uint>(sizeof(uint) + string.size() + sizeof(byte));
}
protected:
@@ -84,43 +76,41 @@ protected:
//
template<typename T>
- static T ConvertEndianessFromSystemToStorage(T Value) {
- if (IsBigEndian()) ReverseByteOrder(&Value);
- return Value;
+ static T convertEndianessFromSystemToStorage(T value) {
+ if (isBigEndian())
+ reverseByteOrder(&value);
+ return value;
}
template<typename T>
- static T ConvertEndianessFromStorageToSystem(T Value) {
- if (IsBigEndian()) ReverseByteOrder(&Value);
- return Value;
+ static T convertEndianessFromStorageToSystem(T value) {
+ if (isBigEndian())
+ reverseByteOrder(&value);
+ return value;
}
private:
- static bool IsBigEndian() {
- uint Dummy = 1;
- byte *DummyPtr = reinterpret_cast<byte *>(&Dummy);
- return DummyPtr[0] == 0;
+ static bool isBigEndian() {
+ uint dummy = 1;
+ byte *dummyPtr = reinterpret_cast<byte *>(&dummy);
+ return dummyPtr[0] == 0;
}
template<typename T>
- static void Swap(T &One, T &Two) {
- T Temp = One;
- One = Two;
- Two = Temp;
+ static void swap(T &one, T &two) {
+ T temp = one;
+ one = two;
+ two = temp;
}
- static void ReverseByteOrder(void *Ptr) {
+ static void reverseByteOrder(void *ptr) {
// Reverses the byte order of the 32-bit word pointed to by Ptr
- byte *CharPtr = static_cast<byte *>(Ptr);
- Swap(CharPtr[0], CharPtr[3]);
- Swap(CharPtr[1], CharPtr[2]);
+ byte *charPtr = static_cast<byte *>(ptr);
+ swap(charPtr[0], charPtr[3]);
+ swap(charPtr[1], charPtr[2]);
}
};
-// -----------------------------------------------------------------------------
-// Compile time asserts
-// -----------------------------------------------------------------------------
-
#define CTASSERT(ex) typedef char ctassert_type[(ex) ? 1 : -1]
CTASSERT(sizeof(byte) == 1);
CTASSERT(sizeof(signed int) == 4);
diff --git a/engines/sword25/kernel/persistenceservice.cpp b/engines/sword25/kernel/persistenceservice.cpp
index 871bc37e2a..0508f2be8f 100644
--- a/engines/sword25/kernel/persistenceservice.cpp
+++ b/engines/sword25/kernel/persistenceservice.cpp
@@ -32,10 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "common/fs.h"
#include "common/savefile.h"
#include "sword25/kernel/kernel.h"
@@ -52,37 +48,30 @@
#define BS_LOG_PREFIX "PERSISTENCESERVICE"
-// -----------------------------------------------------------------------------
-// Constants and utility functions
-// -----------------------------------------------------------------------------
-
namespace Sword25 {
-const char *SAVEGAME_EXTENSION = ".b25s";
-const char *SAVEGAME_DIRECTORY = "saves";
-const char *FILE_MARKER = "BS25SAVEGAME";
-const uint SLOT_COUNT = 18;
-const uint FILE_COPY_BUFFER_SIZE = 1024 * 10;
-const char *VERSIONID = "SCUMMVM1";
-
-// -------------------------------------------------------------------------
-
-Common::String GenerateSavegameFilename(uint slotID) {
- char buffer[10];
- sprintf(buffer, "%d%s", slotID, SAVEGAME_EXTENSION);
- return Common::String(buffer);
-}
-// -------------------------------------------------------------------------
+//static const char *SAVEGAME_EXTENSION = ".b25s";
+static const char *SAVEGAME_DIRECTORY = "saves";
+static const char *FILE_MARKER = "BS25SAVEGAME";
+static const uint SLOT_COUNT = 18;
+static const uint FILE_COPY_BUFFER_SIZE = 1024 * 10;
+static const char *VERSIONID = "SCUMMVM1";
-Common::String GenerateSavegamePath(uint SlotID) {
- Common::FSNode folder(PersistenceService::GetSavegameDirectory());
-
- return folder.getChild(GenerateSavegameFilename(SlotID)).getPath();
+#define MAX_SAVEGAME_SIZE 100
+
+char gameTarget[MAX_SAVEGAME_SIZE];
+
+void setGameTarget(const char *target) {
+ strncpy(gameTarget, target, MAX_SAVEGAME_SIZE);
}
-// -------------------------------------------------------------------------
+static Common::String generateSavegameFilename(uint slotID) {
+ char buffer[MAX_SAVEGAME_SIZE];
+ snprintf(buffer, MAX_SAVEGAME_SIZE, "%s.%.3d", gameTarget, slotID);
+ return Common::String(buffer);
+}
-Common::String FormatTimestamp(TimeDate Time) {
+static Common::String formatTimestamp(TimeDate time) {
// In the original BS2.5 engine, this used a local object to show the date/time as as a string.
// For now in ScummVM it's being hardcoded to 'dd-MON-yyyy hh:mm:ss'
Common::String monthList[12] = {
@@ -90,157 +79,129 @@ Common::String FormatTimestamp(TimeDate Time) {
};
char buffer[100];
snprintf(buffer, 100, "%.2d-%s-%.4d %.2d:%.2d:%.2d",
- Time.tm_mday, monthList[Time.tm_mon].c_str(), 1900 + Time.tm_year,
- Time.tm_hour, Time.tm_min, Time.tm_sec
+ time.tm_mday, monthList[time.tm_mon].c_str(), 1900 + time.tm_year,
+ time.tm_hour, time.tm_min, time.tm_sec
);
return Common::String(buffer);
}
-// -------------------------------------------------------------------------
+static Common::String loadString(Common::InSaveFile *in, uint maxSize = 999) {
+ Common::String result;
-Common::String LoadString(Common::InSaveFile *In, uint MaxSize = 999) {
- Common::String Result;
-
- char ch = (char)In->readByte();
- while ((ch != '\0') && (ch != ' ')) {
- Result += ch;
- if (Result.size() >= MaxSize) break;
- ch = (char)In->readByte();
+ char ch = (char)in->readByte();
+ while (ch != '\0') {
+ result += ch;
+ if (result.size() >= maxSize)
+ break;
+ ch = (char)in->readByte();
}
- return Result;
-}
-
+ return result;
}
-namespace Sword25 {
-
-// -----------------------------------------------------------------------------
-// Private Implementation
-// -----------------------------------------------------------------------------
-
struct SavegameInformation {
- bool IsOccupied;
- bool IsCompatible;
- Common::String Description;
- Common::String Filename;
- uint GamedataLength;
- uint GamedataOffset;
- uint GamedataUncompressedLength;
+ bool isOccupied;
+ bool isCompatible;
+ Common::String description;
+ Common::String filename;
+ uint gamedataLength;
+ uint gamedataOffset;
+ uint gamedataUncompressedLength;
SavegameInformation() {
- Clear();
+ clear();
}
- void Clear() {
- IsOccupied = false;
- IsCompatible = false;
- Description = "";
- Filename = "";
- GamedataLength = 0;
- GamedataOffset = 0;
- GamedataUncompressedLength = 0;
+ void clear() {
+ isOccupied = false;
+ isCompatible = false;
+ description = "";
+ filename = "";
+ gamedataLength = 0;
+ gamedataOffset = 0;
+ gamedataUncompressedLength = 0;
}
};
struct PersistenceService::Impl {
- SavegameInformation m_SavegameInformations[SLOT_COUNT];
-
- // -----------------------------------------------------------------------------
+ SavegameInformation _savegameInformations[SLOT_COUNT];
Impl() {
- ReloadSlots();
+ reloadSlots();
}
- // -----------------------------------------------------------------------------
-
- void ReloadSlots() {
+ void reloadSlots() {
// Über alle Spielstanddateien iterieren und deren Infos einlesen.
for (uint i = 0; i < SLOT_COUNT; ++i) {
- ReadSlotSavegameInformation(i);
+ readSlotSavegameInformation(i);
}
}
- void ReadSlotSavegameInformation(uint SlotID) {
+ void readSlotSavegameInformation(uint slotID) {
// Aktuelle Slotinformationen in den Ausgangszustand versetzen, er wird im Folgenden neu gefüllt.
- SavegameInformation &CurSavegameInfo = m_SavegameInformations[SlotID];
- CurSavegameInfo.Clear();
+ SavegameInformation &curSavegameInfo = _savegameInformations[slotID];
+ curSavegameInfo.clear();
// Den Dateinamen für den Spielstand des Slots generieren.
- Common::String Filename = GenerateSavegameFilename(SlotID);
+ Common::String filename = generateSavegameFilename(slotID);
// Try to open the savegame for loading
Common::SaveFileManager *sfm = g_system->getSavefileManager();
- Common::InSaveFile *File = sfm->openForLoading(Filename);
+ Common::InSaveFile *file = sfm->openForLoading(filename);
- if (File) {
+ if (file) {
// Read in the header
- Common::String StoredMarker = LoadString(File);
- Common::String StoredVersionID = LoadString(File);
- Common::String gameDataLength = LoadString(File);
- CurSavegameInfo.GamedataLength = atoi(gameDataLength.c_str());
- Common::String gamedataUncompressedLength = LoadString(File);
- CurSavegameInfo.GamedataUncompressedLength = atoi(gamedataUncompressedLength.c_str());
+ Common::String storedMarker = loadString(file);
+ Common::String storedVersionID = loadString(file);
+ Common::String gameDescription = loadString(file);
+ Common::String gameDataLength = loadString(file);
+ curSavegameInfo.gamedataLength = atoi(gameDataLength.c_str());
+ Common::String gamedataUncompressedLength = loadString(file);
+ curSavegameInfo.gamedataUncompressedLength = atoi(gamedataUncompressedLength.c_str());
// If the header can be read in and is detected to be valid, we will have a valid file
- if (StoredMarker == FILE_MARKER) {
+ if (storedMarker == FILE_MARKER) {
// Der Slot wird als belegt markiert.
- CurSavegameInfo.IsOccupied = true;
+ curSavegameInfo.isOccupied = true;
// Speichern, ob der Spielstand kompatibel mit der aktuellen Engine-Version ist.
- CurSavegameInfo.IsCompatible = (StoredVersionID == Common::String(VERSIONID));
+ curSavegameInfo.isCompatible = (storedVersionID == Common::String(VERSIONID));
// Dateinamen des Spielstandes speichern.
- CurSavegameInfo.Filename = GenerateSavegameFilename(SlotID);
+ curSavegameInfo.filename = generateSavegameFilename(slotID);
// Die Beschreibung des Spielstandes besteht aus einer textuellen Darstellung des Änderungsdatums der Spielstanddatei.
- CurSavegameInfo.Description = FormatTimestamp(FileSystemUtil::GetInstance().GetFileTime(Filename));
+ curSavegameInfo.description = gameDescription;
// Den Offset zu den gespeicherten Spieldaten innerhalb der Datei speichern.
// Dieses entspricht der aktuellen Position, da nach der letzten Headerinformation noch ein Leerzeichen als trenner folgt.
- CurSavegameInfo.GamedataOffset = static_cast<uint>(File->pos());
+ curSavegameInfo.gamedataOffset = static_cast<uint>(file->pos());
}
- delete File;
+ delete file;
}
}
};
-// -----------------------------------------------------------------------------
-// Construction / Destruction
-// -----------------------------------------------------------------------------
-
-PersistenceService &PersistenceService::GetInstance() {
- static PersistenceService Instance;
- return Instance;
+PersistenceService &PersistenceService::getInstance() {
+ static PersistenceService instance;
+ return instance;
}
-// -----------------------------------------------------------------------------
-
-PersistenceService::PersistenceService() : m_impl(new Impl) {
+PersistenceService::PersistenceService() : _impl(new Impl) {
}
-// -----------------------------------------------------------------------------
-
PersistenceService::~PersistenceService() {
- delete m_impl;
+ delete _impl;
}
-// -----------------------------------------------------------------------------
-// Implementation
-// -----------------------------------------------------------------------------
-
-void PersistenceService::ReloadSlots() {
- m_impl->ReloadSlots();
+void PersistenceService::reloadSlots() {
+ _impl->reloadSlots();
}
-// -----------------------------------------------------------------------------
-
-uint PersistenceService::GetSlotCount() {
+uint PersistenceService::getSlotCount() {
return SLOT_COUNT;
}
-// -----------------------------------------------------------------------------
-
-Common::String PersistenceService::GetSavegameDirectory() {
- Common::FSNode node(FileSystemUtil::GetInstance().GetUserdataDirectory());
+Common::String PersistenceService::getSavegameDirectory() {
+ Common::FSNode node(FileSystemUtil::getUserdataDirectory());
Common::FSNode childNode = node.getChild(SAVEGAME_DIRECTORY);
// Try and return the path using the savegame subfolder. But if doesn't exist, fall back on the data directory
@@ -250,13 +211,11 @@ Common::String PersistenceService::GetSavegameDirectory() {
return node.getPath();
}
-// -----------------------------------------------------------------------------
-
namespace {
-bool CheckSlotID(uint SlotID) {
+bool checkslotID(uint slotID) {
// Überprüfen, ob die Slot-ID zulässig ist.
- if (SlotID >= SLOT_COUNT) {
- BS_LOG_ERRORLN("Tried to access an invalid slot (%d). Only slot ids from 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ if (slotID >= SLOT_COUNT) {
+ BS_LOG_ERRORLN("Tried to access an invalid slot (%d). Only slot ids from 0 to %d are allowed.", slotID, SLOT_COUNT - 1);
return false;
} else {
return true;
@@ -264,143 +223,139 @@ bool CheckSlotID(uint SlotID) {
}
}
-// -----------------------------------------------------------------------------
-
-bool PersistenceService::IsSlotOccupied(uint SlotID) {
- if (!CheckSlotID(SlotID)) return false;
- return m_impl->m_SavegameInformations[SlotID].IsOccupied;
+bool PersistenceService::isSlotOccupied(uint slotID) {
+ if (!checkslotID(slotID))
+ return false;
+ return _impl->_savegameInformations[slotID].isOccupied;
}
-// -----------------------------------------------------------------------------
-
-bool PersistenceService::IsSavegameCompatible(uint SlotID) {
- if (!CheckSlotID(SlotID)) return false;
- return m_impl->m_SavegameInformations[SlotID].IsCompatible;
+bool PersistenceService::isSavegameCompatible(uint slotID) {
+ if (!checkslotID(slotID))
+ return false;
+ return _impl->_savegameInformations[slotID].isCompatible;
}
-// -----------------------------------------------------------------------------
-
-Common::String &PersistenceService::GetSavegameDescription(uint SlotID) {
- static Common::String EmptyString;
- if (!CheckSlotID(SlotID)) return EmptyString;
- return m_impl->m_SavegameInformations[SlotID].Description;
+Common::String &PersistenceService::getSavegameDescription(uint slotID) {
+ static Common::String emptyString;
+ if (!checkslotID(slotID))
+ return emptyString;
+ return _impl->_savegameInformations[slotID].description;
}
-// -----------------------------------------------------------------------------
-
-Common::String &PersistenceService::GetSavegameFilename(uint SlotID) {
- static Common::String EmptyString;
- if (!CheckSlotID(SlotID)) return EmptyString;
- return m_impl->m_SavegameInformations[SlotID].Filename;
+Common::String &PersistenceService::getSavegameFilename(uint slotID) {
+ static Common::String emptyString;
+ if (!checkslotID(slotID))
+ return emptyString;
+ return _impl->_savegameInformations[slotID].filename;
}
-// -----------------------------------------------------------------------------
+bool PersistenceService::saveGame(uint slotID, const Common::String &screenshotFilename) {
+ // FIXME: This code is a hack which bypasses the savefile API,
+ // and should eventually be removed.
-bool PersistenceService::SaveGame(uint SlotID, const Common::String &ScreenshotFilename) {
// Überprüfen, ob die Slot-ID zulässig ist.
- if (SlotID >= SLOT_COUNT) {
- BS_LOG_ERRORLN("Tried to save to an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ if (slotID >= SLOT_COUNT) {
+ BS_LOG_ERRORLN("Tried to save to an invalid slot (%d). Only slot ids form 0 to %d are allowed.", slotID, SLOT_COUNT - 1);
return false;
}
// Dateinamen erzeugen.
- Common::String Filename = GenerateSavegameFilename(SlotID);
-
- // Sicherstellen, dass das Verzeichnis für die Spielstanddateien existiert.
- FileSystemUtil::GetInstance().CreateDirectory(GetSavegameDirectory());
+ Common::String filename = generateSavegameFilename(slotID);
// Spielstanddatei öffnen und die Headerdaten schreiben.
Common::SaveFileManager *sfm = g_system->getSavefileManager();
- Common::OutSaveFile *File = sfm->openForSaving(Filename);
+ Common::OutSaveFile *file = sfm->openForSaving(filename);
- File->writeString(FILE_MARKER);
- File->writeByte(' ');
- File->writeString(VERSIONID);
- File->writeByte(' ');
+ file->writeString(FILE_MARKER);
+ file->writeByte(0);
+ file->writeString(VERSIONID);
+ file->writeByte(0);
- if (File->err()) {
- error("Unable to write header data to savegame file \"%s\".", Filename.c_str());
+ TimeDate dt;
+ g_system->getTimeAndDate(dt);
+ file->writeString(formatTimestamp(dt));
+ file->writeByte(0);
+
+ if (file->err()) {
+ error("Unable to write header data to savegame file \"%s\".", filename.c_str());
}
// Alle notwendigen Module persistieren.
- OutputPersistenceBlock Writer;
- bool Success = true;
- Success &= Kernel::GetInstance()->GetScript()->persist(Writer);
- Success &= RegionRegistry::getInstance().persist(Writer);
- Success &= Kernel::GetInstance()->GetGfx()->persist(Writer);
- Success &= Kernel::GetInstance()->GetSfx()->persist(Writer);
- Success &= Kernel::GetInstance()->GetInput()->persist(Writer);
- if (!Success) {
- error("Unable to persist modules for savegame file \"%s\".", Filename.c_str());
+ OutputPersistenceBlock writer;
+ bool success = true;
+ success &= Kernel::getInstance()->getScript()->persist(writer);
+ success &= RegionRegistry::instance().persist(writer);
+ success &= Kernel::getInstance()->getGfx()->persist(writer);
+ success &= Kernel::getInstance()->getSfx()->persist(writer);
+ success &= Kernel::getInstance()->getInput()->persist(writer);
+ if (!success) {
+ error("Unable to persist modules for savegame file \"%s\".", filename.c_str());
}
// Daten komprimieren.
- uLongf CompressedLength = Writer.GetDataSize() + (Writer.GetDataSize() + 500) / 1000 + 12;
- Bytef *CompressionBuffer = new Bytef[CompressedLength];
+ uLongf compressedLength = writer.getDataSize() + (writer.getDataSize() + 500) / 1000 + 12;
+ Bytef *compressionBuffer = new Bytef[compressedLength];
- if (compress2(&CompressionBuffer[0], &CompressedLength, reinterpret_cast<const Bytef *>(Writer.GetData()), Writer.GetDataSize(), 6) != Z_OK) {
- error("Unable to compress savegame data in savegame file \"%s\".", Filename.c_str());
+ if (compress2(&compressionBuffer[0], &compressedLength, reinterpret_cast<const Bytef *>(writer.getData()), writer.getDataSize(), 6) != Z_OK) {
+ error("Unable to compress savegame data in savegame file \"%s\".", filename.c_str());
}
// Länge der komprimierten Daten und der unkomprimierten Daten in die Datei schreiben.
char sBuffer[10];
- snprintf(sBuffer, 10, "%ld", CompressedLength);
- File->writeString(sBuffer);
- File->writeByte(' ');
- snprintf(sBuffer, 10, "%u", Writer.GetDataSize());
- File->writeString(sBuffer);
- File->writeByte(' ');
+ snprintf(sBuffer, 10, "%ld", compressedLength);
+ file->writeString(sBuffer);
+ file->writeByte(0);
+ snprintf(sBuffer, 10, "%u", writer.getDataSize());
+ file->writeString(sBuffer);
+ file->writeByte(0);
// Komprimierte Daten in die Datei schreiben.
- File->write(reinterpret_cast<char *>(&CompressionBuffer[0]), CompressedLength);
- if (File->err()) {
- error("Unable to write game data to savegame file \"%s\".", Filename.c_str());
+ file->write(reinterpret_cast<char *>(&compressionBuffer[0]), compressedLength);
+ if (file->err()) {
+ error("Unable to write game data to savegame file \"%s\".", filename.c_str());
}
// Get the screenshot
- Common::MemoryReadStream *thumbnail = (static_cast<GraphicEngine *>(
- Kernel::GetInstance()->GetService("gfx")))->getThumbnail();
+ Common::SeekableReadStream *thumbnail = Kernel::getInstance()->getGfx()->getThumbnail();
if (thumbnail) {
- byte *Buffer = new Byte[FILE_COPY_BUFFER_SIZE];
+ byte *buffer = new Byte[FILE_COPY_BUFFER_SIZE];
while (!thumbnail->eos()) {
- int bytesRead = thumbnail->read(&Buffer[0], FILE_COPY_BUFFER_SIZE);
- File->write(&Buffer[0], bytesRead);
+ int bytesRead = thumbnail->read(&buffer[0], FILE_COPY_BUFFER_SIZE);
+ file->write(&buffer[0], bytesRead);
}
- delete[] Buffer;
+ delete[] buffer;
} else {
- BS_LOG_WARNINGLN("The screenshot file \"%s\" does not exist. Savegame is written without a screenshot.", Filename.c_str());
+ BS_LOG_WARNINGLN("The screenshot file \"%s\" does not exist. Savegame is written without a screenshot.", filename.c_str());
}
- // Savegameinformationen für diesen Slot aktualisieren.
- m_impl->ReadSlotSavegameInformation(SlotID);
+ file->finalize();
+ delete file;
+ delete[] compressionBuffer;
- File->finalize();
- delete File;
- delete[] CompressionBuffer;
+ // Savegameinformationen für diesen Slot aktualisieren.
+ _impl->readSlotSavegameInformation(slotID);
// Erfolg signalisieren.
return true;
}
-// -----------------------------------------------------------------------------
-
-bool PersistenceService::LoadGame(uint SlotID) {
+bool PersistenceService::loadGame(uint slotID) {
Common::SaveFileManager *sfm = g_system->getSavefileManager();
- Common::InSaveFile *File;
+ Common::InSaveFile *file;
// Überprüfen, ob die Slot-ID zulässig ist.
- if (SlotID >= SLOT_COUNT) {
- BS_LOG_ERRORLN("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ if (slotID >= SLOT_COUNT) {
+ BS_LOG_ERRORLN("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", slotID, SLOT_COUNT - 1);
return false;
}
- SavegameInformation &CurSavegameInfo = m_impl->m_SavegameInformations[SlotID];
+ SavegameInformation &curSavegameInfo = _impl->_savegameInformations[slotID];
// Überprüfen, ob der Slot belegt ist.
- if (!CurSavegameInfo.IsOccupied) {
- BS_LOG_ERRORLN("Tried to load from an empty slot (%d).", SlotID);
+ if (!curSavegameInfo.isOccupied) {
+ BS_LOG_ERRORLN("Tried to load from an empty slot (%d).", slotID);
return false;
}
@@ -408,54 +363,54 @@ bool PersistenceService::LoadGame(uint SlotID) {
// Im Debug-Modus wird dieser Test übersprungen. Für das Testen ist es hinderlich auf die Einhaltung dieser strengen Bedingung zu bestehen,
// da sich die Versions-ID bei jeder Codeänderung mitändert.
#ifndef DEBUG
- if (!CurSavegameInfo.IsCompatible) {
- BS_LOG_ERRORLN("Tried to load a savegame (%d) that is not compatible with this engine version.", SlotID);
+ if (!curSavegameInfo.isCompatible) {
+ BS_LOG_ERRORLN("Tried to load a savegame (%d) that is not compatible with this engine version.", slotID);
return false;
}
#endif
- byte *CompressedDataBuffer = new byte[CurSavegameInfo.GamedataLength];
- byte *UncompressedDataBuffer = new Bytef[CurSavegameInfo.GamedataUncompressedLength];
+ byte *compressedDataBuffer = new byte[curSavegameInfo.gamedataLength];
+ byte *uncompressedDataBuffer = new Bytef[curSavegameInfo.gamedataUncompressedLength];
- File = sfm->openForLoading(GenerateSavegameFilename(SlotID));
+ file = sfm->openForLoading(generateSavegameFilename(slotID));
- File->seek(CurSavegameInfo.GamedataOffset);
- File->read(reinterpret_cast<char *>(&CompressedDataBuffer[0]), CurSavegameInfo.GamedataLength);
- if (File->err()) {
- BS_LOG_ERRORLN("Unable to load the gamedata from the savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
- delete[] CompressedDataBuffer;
- delete[] UncompressedDataBuffer;
+ file->seek(curSavegameInfo.gamedataOffset);
+ file->read(reinterpret_cast<char *>(&compressedDataBuffer[0]), curSavegameInfo.gamedataLength);
+ if (file->err()) {
+ BS_LOG_ERRORLN("Unable to load the gamedata from the savegame file \"%s\".", curSavegameInfo.filename.c_str());
+ delete[] compressedDataBuffer;
+ delete[] uncompressedDataBuffer;
return false;
}
// Spieldaten dekomprimieren.
- uLongf UncompressedBufferSize = CurSavegameInfo.GamedataUncompressedLength;
- if (uncompress(reinterpret_cast<Bytef *>(&UncompressedDataBuffer[0]), &UncompressedBufferSize,
- reinterpret_cast<Bytef *>(&CompressedDataBuffer[0]), CurSavegameInfo.GamedataLength) != Z_OK) {
- BS_LOG_ERRORLN("Unable to decompress the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
- delete[] UncompressedDataBuffer;
- delete[] CompressedDataBuffer;
- delete File;
+ uLongf uncompressedBufferSize = curSavegameInfo.gamedataUncompressedLength;
+ if (uncompress(reinterpret_cast<Bytef *>(&uncompressedDataBuffer[0]), &uncompressedBufferSize,
+ reinterpret_cast<Bytef *>(&compressedDataBuffer[0]), curSavegameInfo.gamedataLength) != Z_OK) {
+ BS_LOG_ERRORLN("Unable to decompress the gamedata from savegame file \"%s\".", curSavegameInfo.filename.c_str());
+ delete[] uncompressedDataBuffer;
+ delete[] compressedDataBuffer;
+ delete file;
return false;
}
- InputPersistenceBlock Reader(&UncompressedDataBuffer[0], CurSavegameInfo.GamedataUncompressedLength);
+ InputPersistenceBlock reader(&uncompressedDataBuffer[0], curSavegameInfo.gamedataUncompressedLength);
// Einzelne Engine-Module depersistieren.
- bool Success = true;
- Success &= Kernel::GetInstance()->GetScript()->unpersist(Reader);
+ bool success = true;
+ success &= Kernel::getInstance()->getScript()->unpersist(reader);
// Muss unbedingt nach Script passieren. Da sonst die bereits wiederhergestellten Regions per Garbage-Collection gekillt werden.
- Success &= RegionRegistry::getInstance().unpersist(Reader);
- Success &= Kernel::GetInstance()->GetGfx()->unpersist(Reader);
- Success &= Kernel::GetInstance()->GetSfx()->unpersist(Reader);
- Success &= Kernel::GetInstance()->GetInput()->unpersist(Reader);
+ success &= RegionRegistry::instance().unpersist(reader);
+ success &= Kernel::getInstance()->getGfx()->unpersist(reader);
+ success &= Kernel::getInstance()->getSfx()->unpersist(reader);
+ success &= Kernel::getInstance()->getInput()->unpersist(reader);
- delete[] CompressedDataBuffer;
- delete[] UncompressedDataBuffer;
- delete File;
+ delete[] compressedDataBuffer;
+ delete[] uncompressedDataBuffer;
+ delete file;
- if (!Success) {
- BS_LOG_ERRORLN("Unable to unpersist the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ if (!success) {
+ BS_LOG_ERRORLN("Unable to unpersist the gamedata from savegame file \"%s\".", curSavegameInfo.filename.c_str());
return false;
}
diff --git a/engines/sword25/kernel/persistenceservice.h b/engines/sword25/kernel/persistenceservice.h
index d14185eac2..0db109d1b0 100644
--- a/engines/sword25/kernel/persistenceservice.h
+++ b/engines/sword25/kernel/persistenceservice.h
@@ -35,18 +35,10 @@
#ifndef SWORD25_PERSISTENCESERVICE_H
#define SWORD25_PERSISTENCESERVICE_H
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "sword25/kernel/common.h"
namespace Sword25 {
-// -----------------------------------------------------------------------------
-// Class declaration
-// -----------------------------------------------------------------------------
-
class PersistenceService {
public:
PersistenceService();
@@ -56,29 +48,31 @@ public:
// Singleton Method
// -----------------------------------------------------------------------------
- static PersistenceService &GetInstance();
+ static PersistenceService &getInstance();
// -----------------------------------------------------------------------------
// Interface
// -----------------------------------------------------------------------------
- static uint GetSlotCount();
- static Common::String GetSavegameDirectory();
+ static uint getSlotCount();
+ static Common::String getSavegameDirectory();
- void ReloadSlots();
- bool IsSlotOccupied(uint SlotID);
- bool IsSavegameCompatible(uint SlotID);
- Common::String &GetSavegameDescription(uint SlotID);
- Common::String &GetSavegameFilename(uint SlotID);
+ void reloadSlots();
+ bool isSlotOccupied(uint slotID);
+ bool isSavegameCompatible(uint slotID);
+ Common::String &getSavegameDescription(uint slotID);
+ Common::String &getSavegameFilename(uint slotID);
- bool SaveGame(uint SlotID, const Common::String &ScreenshotFilename);
- bool LoadGame(uint SlotID);
+ bool saveGame(uint slotID, const Common::String &screenshotFilename);
+ bool loadGame(uint slotID);
private:
struct Impl;
- Impl *m_impl;
+ Impl *_impl;
};
+void setGameTarget(const char *target);
+
} // End of namespace Sword25
#endif
diff --git a/engines/sword25/kernel/resmanager.cpp b/engines/sword25/kernel/resmanager.cpp
index 9e80f32f8d..1979e6e6c6 100644
--- a/engines/sword25/kernel/resmanager.cpp
+++ b/engines/sword25/kernel/resmanager.cpp
@@ -36,7 +36,6 @@
#include "sword25/kernel/resource.h"
#include "sword25/kernel/resservice.h"
-#include "sword25/kernel/string.h"
#include "sword25/package/packagemanager.h"
namespace Sword25 {
@@ -45,60 +44,35 @@ namespace Sword25 {
ResourceManager::~ResourceManager() {
// Clear all unlocked resources
- EmptyCache();
+ emptyCache();
// All remaining resources are not released, so print warnings and release
- Common::List<Resource *>::iterator Iter = m_Resources.begin();
- for (; Iter != m_Resources.end(); ++Iter) {
- BS_LOG_WARNINGLN("Resource \"%s\" was not released.", (*Iter)->getFileName().c_str());
+ Common::List<Resource *>::iterator iter = _resources.begin();
+ for (; iter != _resources.end(); ++iter) {
+ BS_LOG_WARNINGLN("Resource \"%s\" was not released.", (*iter)->getFileName().c_str());
// Set the lock count to zero
- while ((*Iter)->GetLockCount() > 0) {
- (*Iter)->release();
+ while ((*iter)->getLockCount() > 0) {
+ (*iter)->release();
};
// Delete the resource
- delete(*Iter);
+ delete(*iter);
}
}
/**
- * Returns a resource by it's ordinal index. Returns NULL if any error occurs
- * Note: This method is not optimised for speed and should be used only for debugging purposes
- * @param Ord Ordinal number of the resource. Must be between 0 and GetResourceCount() - 1.
- */
-Resource *ResourceManager::GetResourceByOrdinal(int Ord) const {
- // Überprüfen ob der Index Ord innerhald der Listengrenzen liegt.
- if (Ord < 0 || Ord >= GetResourceCount()) {
- BS_LOG_ERRORLN("Resource ordinal (%d) out of bounds (0 - %d).", Ord, GetResourceCount() - 1);
- return NULL;
- }
-
- // Liste durchlaufen und die Resource mit dem gewünschten Index zurückgeben.
- int CurOrd = 0;
- Common::List<Resource *>::const_iterator Iter = m_Resources.begin();
- for (; Iter != m_Resources.end(); ++Iter, ++CurOrd) {
- if (CurOrd == Ord)
- return (*Iter);
- }
-
- // Die Ausführung sollte nie an diesem Punkt ankommen.
- BS_LOG_EXTERRORLN("Execution reached unexpected point.");
- return NULL;
-}
-
-/**
* Registers a RegisterResourceService. This method is the constructor of
* BS_ResourceService, and thus helps all resource services in the ResourceManager list
* @param pService Which service
*/
-bool ResourceManager::RegisterResourceService(ResourceService *pService) {
+bool ResourceManager::registerResourceService(ResourceService *pService) {
if (!pService) {
BS_LOG_ERRORLN("Can't register NULL resource service.");
return false;
}
- m_ResourceServices.push_back(pService);
+ _resourceServices.push_back(pService);
return true;
}
@@ -106,34 +80,36 @@ bool ResourceManager::RegisterResourceService(ResourceService *pService) {
/**
* Deletes resources as necessary until the specified memory limit is not being exceeded.
*/
-void ResourceManager::DeleteResourcesIfNecessary() {
+void ResourceManager::deleteResourcesIfNecessary() {
// If enough memory is available, or no resources are loaded, then the function can immediately end
- if (m_KernelPtr->GetUsedMemory() < m_MaxMemoryUsage || m_Resources.empty()) return;
+ if (_kernelPtr->getUsedMemory() < _maxMemoryUsage || _resources.empty())
+ return;
// Keep deleting resources until the memory usage of the process falls below the set maximum limit.
// The list is processed backwards in order to first release those resources who have been
// not been accessed for the longest
- Common::List<Resource *>::iterator Iter = m_Resources.end();
+ Common::List<Resource *>::iterator iter = _resources.end();
do {
- --Iter;
+ --iter;
// The resource may be released only if it isn't locked
- if ((*Iter)->GetLockCount() == 0) Iter = DeleteResource(*Iter);
- } while (Iter != m_Resources.begin() && m_KernelPtr->GetUsedMemory() > m_MaxMemoryUsage);
+ if ((*iter)->getLockCount() == 0)
+ iter = deleteResource(*iter);
+ } while (iter != _resources.begin() && _kernelPtr->getUsedMemory() > _maxMemoryUsage);
}
/**
* Releases all resources that are not locked.
- **/
-void ResourceManager::EmptyCache() {
+ */
+void ResourceManager::emptyCache() {
// Scan through the resource list
- Common::List<Resource *>::iterator Iter = m_Resources.begin();
- while (Iter != m_Resources.end()) {
- if ((*Iter)->GetLockCount() == 0) {
+ Common::List<Resource *>::iterator iter = _resources.begin();
+ while (iter != _resources.end()) {
+ if ((*iter)->getLockCount() == 0) {
// Delete the resource
- Iter = DeleteResource(*Iter);
+ iter = deleteResource(*iter);
} else
- ++Iter;
+ ++iter;
}
}
@@ -141,29 +117,30 @@ void ResourceManager::EmptyCache() {
* Returns a requested resource. If any error occurs, returns NULL
* @param FileName Filename of resource
*/
-Resource *ResourceManager::RequestResource(const Common::String &FileName) {
+Resource *ResourceManager::requestResource(const Common::String &fileName) {
// Get the absolute path to the file
- Common::String UniqueFileName = GetUniqueFileName(FileName);
- if (UniqueFileName == "")
+ Common::String uniqueFileName = getUniqueFileName(fileName);
+ if (uniqueFileName.empty())
return NULL;
// Determine whether the resource is already loaded
// If the resource is found, it will be placed at the head of the resource list and returned
{
- Resource *pResource = GetResource(UniqueFileName);
+ Resource *pResource = getResource(uniqueFileName);
if (pResource) {
- MoveToFront(pResource);
- (pResource)->AddReference();
+ moveToFront(pResource);
+ (pResource)->addReference();
return pResource;
}
}
// The resource was not found, therefore, must not be loaded yet
- if (m_LogCacheMiss) BS_LOG_WARNINGLN("\"%s\" was not precached.", UniqueFileName.c_str());
+ if (_logCacheMiss)
+ BS_LOG_WARNINGLN("\"%s\" was not precached.", uniqueFileName.c_str());
- Resource *pResource;
- if ((pResource = loadResource(UniqueFileName))) {
- pResource->AddReference();
+ Resource *pResource = loadResource(uniqueFileName);
+ if (pResource) {
+ pResource->addReference();
return pResource;
}
@@ -176,26 +153,26 @@ Resource *ResourceManager::RequestResource(const Common::String &FileName) {
* @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
* This is useful for files that may have changed in the interim
*/
-bool ResourceManager::PrecacheResource(const Common::String &FileName, bool ForceReload) {
+bool ResourceManager::precacheResource(const Common::String &fileName, bool forceReload) {
// Get the absolute path to the file
- Common::String UniqueFileName = GetUniqueFileName(FileName);
- if (UniqueFileName == "")
+ Common::String uniqueFileName = getUniqueFileName(fileName);
+ if (uniqueFileName.empty())
return false;
- Resource *ResourcePtr = GetResource(UniqueFileName);
+ Resource *resourcePtr = getResource(uniqueFileName);
- if (ForceReload && ResourcePtr) {
- if (ResourcePtr->GetLockCount()) {
- BS_LOG_ERRORLN("Could not force precaching of \"%s\". The resource is locked.", FileName.c_str());
+ if (forceReload && resourcePtr) {
+ if (resourcePtr->getLockCount()) {
+ BS_LOG_ERRORLN("Could not force precaching of \"%s\". The resource is locked.", fileName.c_str());
return false;
} else {
- DeleteResource(ResourcePtr);
- ResourcePtr = 0;
+ deleteResource(resourcePtr);
+ resourcePtr = 0;
}
}
- if (!ResourcePtr && loadResource(UniqueFileName) == NULL) {
- BS_LOG_ERRORLN("Could not precache \"%s\",", FileName.c_str());
+ if (!resourcePtr && loadResource(uniqueFileName) == NULL) {
+ BS_LOG_ERRORLN("Could not precache \"%s\",", fileName.c_str());
return false;
}
@@ -206,13 +183,13 @@ bool ResourceManager::PrecacheResource(const Common::String &FileName, bool Forc
* Moves a resource to the top of the resource list
* @param pResource The resource
*/
-void ResourceManager::MoveToFront(Resource *pResource) {
+void ResourceManager::moveToFront(Resource *pResource) {
// Erase the resource from it's current position
- m_Resources.erase(pResource->_iterator);
+ _resources.erase(pResource->_iterator);
// Re-add the resource at the front of the list
- m_Resources.push_front(pResource);
+ _resources.push_front(pResource);
// Reset the resource iterator to the repositioned item
- pResource->_iterator = m_Resources.begin();
+ pResource->_iterator = _resources.begin();
}
/**
@@ -223,24 +200,24 @@ void ResourceManager::MoveToFront(Resource *pResource) {
*/
Resource *ResourceManager::loadResource(const Common::String &fileName) {
// ResourceService finden, der die Resource laden kann.
- for (uint i = 0; i < m_ResourceServices.size(); ++i) {
- if (m_ResourceServices[i]->canLoadResource(fileName)) {
+ for (uint i = 0; i < _resourceServices.size(); ++i) {
+ if (_resourceServices[i]->canLoadResource(fileName)) {
// If more memory is desired, memory must be released
- DeleteResourcesIfNecessary();
+ deleteResourcesIfNecessary();
// Load the resource
- Resource *pResource;
- if (!(pResource = m_ResourceServices[i]->loadResource(fileName))) {
+ Resource *pResource = _resourceServices[i]->loadResource(fileName);
+ if (!pResource) {
BS_LOG_ERRORLN("Responsible service could not load resource \"%s\".", fileName.c_str());
return NULL;
}
// Add the resource to the front of the list
- m_Resources.push_front(pResource);
- pResource->_iterator = m_Resources.begin();
+ _resources.push_front(pResource);
+ pResource->_iterator = _resources.begin();
// Also store the resource in the hash table for quick lookup
- m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].push_front(pResource);
+ _resourceHashMap[pResource->getFileName()] = pResource;
return pResource;
}
@@ -254,70 +231,60 @@ Resource *ResourceManager::loadResource(const Common::String &fileName) {
* Returns the full path of a given resource filename.
* It will return an empty string if a path could not be created.
*/
-Common::String ResourceManager::GetUniqueFileName(const Common::String &FileName) const {
+Common::String ResourceManager::getUniqueFileName(const Common::String &fileName) const {
// Get a pointer to the package manager
- PackageManager *pPackage = (PackageManager *)m_KernelPtr->GetService("package");
+ PackageManager *pPackage = (PackageManager *)_kernelPtr->getPackage();
if (!pPackage) {
BS_LOG_ERRORLN("Could not get package manager.");
- return Common::String("");
+ return Common::String();
}
// Absoluten Pfad der Datei bekommen und somit die Eindeutigkeit des Dateinamens sicherstellen
- Common::String UniqueFileName = pPackage->getAbsolutePath(FileName);
- if (UniqueFileName == "")
- BS_LOG_ERRORLN("Could not create absolute file name for \"%s\".", FileName.c_str());
+ Common::String uniquefileName = pPackage->getAbsolutePath(fileName);
+ if (uniquefileName.empty())
+ BS_LOG_ERRORLN("Could not create absolute file name for \"%s\".", fileName.c_str());
- return UniqueFileName;
+ return uniquefileName;
}
/**
* Deletes a resource, removes it from the lists, and updates m_UsedMemory
*/
-Common::List<Resource *>::iterator ResourceManager::DeleteResource(Resource *pResource) {
+Common::List<Resource *>::iterator ResourceManager::deleteResource(Resource *pResource) {
// Remove the resource from the hash table
- m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].remove(pResource);
-
- Resource *pDummy = pResource;
+ _resourceHashMap.erase(pResource->_fileName);
// Delete the resource from the resource list
- Common::List<Resource *>::iterator Result = m_Resources.erase(pResource->_iterator);
+ Common::List<Resource *>::iterator result = _resources.erase(pResource->_iterator);
// Delete the resource
- delete(pDummy);
+ delete pResource;
// Return the iterator
- return Result;
+ return result;
}
/**
* Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
- * @param UniqueFileName The absolute path and filename
- * Gibt einen Pointer auf die angeforderte Resource zurück, oder NULL, wenn die Resourcen nicht geladen ist.
+ * @param UniquefileName The absolute path and filename
*/
-Resource *ResourceManager::GetResource(const Common::String &UniqueFileName) const {
+Resource *ResourceManager::getResource(const Common::String &uniquefileName) const {
// Determine whether the resource is already loaded
- const Common::List<Resource *>& HashBucket = m_ResourceHashTable[
- BS_String::GetHash(UniqueFileName) % HASH_TABLE_BUCKETS];
- {
- Common::List<Resource *>::const_iterator Iter = HashBucket.begin();
- for (; Iter != HashBucket.end(); ++Iter) {
- // Wenn die Resource gefunden wurde wird sie zurückgegeben.
- if ((*Iter)->getFileName() == UniqueFileName)
- return *Iter;
- }
- }
+ ResMap::iterator it = _resourceHashMap.find(uniquefileName);
+ if (it != _resourceHashMap.end())
+ return it->_value;
- // Resource wurde nicht gefunden, ist also nicht geladen
+ // Resource was not found, i.e. has not yet been loaded.
return NULL;
}
/**
* Writes the names of all currently locked resources to the log file
*/
-void ResourceManager::DumpLockedResources() {
- for (Common::List<Resource *>::iterator Iter = m_Resources.begin(); Iter != m_Resources.end(); ++Iter) {
- if ((*Iter)->GetLockCount() > 0) {
- BS_LOGLN("%s", (*Iter)->getFileName().c_str());
+void ResourceManager::dumpLockedResources() {
+ for (Common::List<Resource *>::iterator iter = _resources.begin(); iter != _resources.end(); ++iter) {
+ if ((*iter)->getLockCount() > 0) {
+ BS_LOGLN("%s", (*iter)->getFileName().c_str());
}
}
}
@@ -328,9 +295,9 @@ void ResourceManager::DumpLockedResources() {
* as a guideline, and not as a fixed boundary. It is not guaranteed not to be exceeded;
* the whole game engine may still use more memory than any amount specified.
*/
-void ResourceManager::SetMaxMemoryUsage(uint MaxMemoryUsage) {
- m_MaxMemoryUsage = MaxMemoryUsage;
- DeleteResourcesIfNecessary();
+void ResourceManager::setMaxMemoryUsage(uint maxMemoryUsage) {
+ _maxMemoryUsage = maxMemoryUsage;
+ deleteResourcesIfNecessary();
}
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/resmanager.h b/engines/sword25/kernel/resmanager.h
index 578f121fec..613bb3a3a2 100644
--- a/engines/sword25/kernel/resmanager.h
+++ b/engines/sword25/kernel/resmanager.h
@@ -35,14 +35,14 @@
#ifndef SWORD25_RESOURCEMANAGER_H
#define SWORD25_RESOURCEMANAGER_H
-// Includes
#include "common/list.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
#include "sword25/kernel/common.h"
namespace Sword25 {
-// Class definitions
class ResourceService;
class Resource;
class Kernel;
@@ -55,7 +55,7 @@ public:
* Returns a requested resource. If any error occurs, returns NULL
* @param FileName Filename of resource
*/
- Resource *RequestResource(const Common::String &FileName);
+ Resource *requestResource(const Common::String &fileName);
/**
* Loads a resource into the cache
@@ -63,39 +63,25 @@ public:
* @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
* This is useful for files that may have changed in the interim
*/
- bool PrecacheResource(const Common::String &FileName, bool ForceReload = false);
-
- /**
- * Returns the number of loaded resources
- */
- int GetResourceCount() const {
- return static_cast<int>(m_Resources.size());
- }
-
- /**
- * Returns a resource by it's ordinal index. Returns NULL if any error occurs
- * Note: This method is not optimised for speed and should be used only for debugging purposes
- * @param Ord Ordinal number of the resource. Must be between 0 and GetResourceCount() - 1.
- */
- Resource *GetResourceByOrdinal(int Ord) const;
+ bool precacheResource(const Common::String &fileName, bool forceReload = false);
/**
* Registers a RegisterResourceService. This method is the constructor of
* BS_ResourceService, and thus helps all resource services in the ResourceManager list
* @param pService Which service
*/
- bool RegisterResourceService(ResourceService *pService);
+ bool registerResourceService(ResourceService *pService);
/**
* Releases all resources that are not locked.
**/
- void EmptyCache();
+ void emptyCache();
/**
* Returns the maximum memory the kernel has used
*/
- int GetMaxMemoryUsage() const {
- return m_MaxMemoryUsage;
+ int getMaxMemoryUsage() const {
+ return _maxMemoryUsage;
}
/**
@@ -104,28 +90,28 @@ public:
* as a guideline, and not as a fixed boundary. It is not guaranteed not to be exceeded;
* the whole game engine may still use more memory than any amount specified.
*/
- void SetMaxMemoryUsage(uint MaxMemoryUsage);
+ void setMaxMemoryUsage(uint maxMemoryUsage);
/**
* Specifies whether a warning is written to the log when a cache miss occurs.
* THe default value is "false".
*/
- bool IsLogCacheMiss() const {
- return m_LogCacheMiss;
+ bool isLogCacheMiss() const {
+ return _logCacheMiss;
}
/**
* Sets whether warnings are written to the log if a cache miss occurs.
* @param Flag If "true", then future warnings will be logged
*/
- void SetLogCacheMiss(bool Flag) {
- m_LogCacheMiss = Flag;
+ void setLogCacheMiss(bool flag) {
+ _logCacheMiss = flag;
}
/**
* Writes the names of all currently locked resources to the log file
*/
- void DumpLockedResources();
+ void dumpLockedResources();
private:
/**
@@ -133,21 +119,17 @@ private:
* Only the BS_Kernel class can generate copies this class. Thus, the constructor is private
*/
ResourceManager(Kernel *pKernel) :
- m_KernelPtr(pKernel),
- m_MaxMemoryUsage(100000000),
- m_LogCacheMiss(false)
- {};
+ _kernelPtr(pKernel),
+ _maxMemoryUsage(100000000),
+ _logCacheMiss(false)
+ {}
virtual ~ResourceManager();
- enum {
- HASH_TABLE_BUCKETS = 256
- };
-
/**
* Moves a resource to the top of the resource list
* @param pResource The resource
*/
- void MoveToFront(Resource *pResource);
+ void moveToFront(Resource *pResource);
/**
* Loads a resource and updates the m_UsedMemory total
@@ -161,31 +143,31 @@ private:
* Returns the full path of a given resource filename.
* It will return an empty string if a path could not be created.
*/
- Common::String GetUniqueFileName(const Common::String &FileName) const;
+ Common::String getUniqueFileName(const Common::String &fileName) const;
/**
* Deletes a resource, removes it from the lists, and updates m_UsedMemory
*/
- Common::List<Resource *>::iterator DeleteResource(Resource *pResource);
+ Common::List<Resource *>::iterator deleteResource(Resource *pResource);
/**
* Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
* @param UniqueFileName The absolute path and filename
- * Gibt einen Pointer auf die angeforderte Resource zurück, oder NULL, wenn die Resourcen nicht geladen ist.
*/
- Resource *GetResource(const Common::String &UniqueFileName) const;
+ Resource *getResource(const Common::String &uniqueFileName) const;
/**
* Deletes resources as necessary until the specified memory limit is not being exceeded.
*/
- void DeleteResourcesIfNecessary();
-
- Kernel *m_KernelPtr;
- uint m_MaxMemoryUsage;
- Common::Array<ResourceService *> m_ResourceServices;
- Common::List<Resource *> m_Resources;
- Common::List<Resource *> m_ResourceHashTable[HASH_TABLE_BUCKETS];
- bool m_LogCacheMiss;
+ void deleteResourcesIfNecessary();
+
+ Kernel *_kernelPtr;
+ uint _maxMemoryUsage;
+ Common::Array<ResourceService *> _resourceServices;
+ Common::List<Resource *> _resources;
+ typedef Common::HashMap<Common::String, Resource *> ResMap;
+ ResMap _resourceHashMap;
+ bool _logCacheMiss;
};
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/resource.cpp b/engines/sword25/kernel/resource.cpp
index f6f4f13f68..40eea2138c 100644
--- a/engines/sword25/kernel/resource.cpp
+++ b/engines/sword25/kernel/resource.cpp
@@ -33,7 +33,6 @@
*/
#include "sword25/kernel/resource.h"
-#include "sword25/kernel/string.h"
#include "sword25/kernel/kernel.h"
#include "sword25/package/packagemanager.h"
@@ -44,10 +43,10 @@ namespace Sword25 {
Resource::Resource(const Common::String &fileName, RESOURCE_TYPES type) :
_type(type),
_refCount(0) {
- BS_ASSERT(Kernel::GetInstance()->GetService("package"));
+ PackageManager *pPM = Kernel::getInstance()->getPackage();
+ BS_ASSERT(pPM);
- _fileName = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"))->getAbsolutePath(fileName);
- _fileNameHash = BS_String::GetHash(fileName);
+ _fileName = pPM->getAbsolutePath(fileName);
}
void Resource::release() {
diff --git a/engines/sword25/kernel/resource.h b/engines/sword25/kernel/resource.h
index 2a4d197138..5c6108a281 100644
--- a/engines/sword25/kernel/resource.h
+++ b/engines/sword25/kernel/resource.h
@@ -62,7 +62,7 @@ public:
* Prevents the resource from being released.
* @remarks This method allows a resource to be locked multiple times.
**/
- void AddReference() {
+ void addReference() {
++_refCount;
}
@@ -77,7 +77,7 @@ public:
* Returns the current lock count for the resource
* @return The current lock count
**/
- int GetLockCount() const {
+ int getLockCount() const {
return _refCount;
}
@@ -89,27 +89,19 @@ public:
}
/**
- * Returns the hash of the filename of a resource
- */
- uint GetFileNameHash() const {
- return _fileNameHash;
- }
-
- /**
* Returns a resource's type
*/
- uint GetType() const {
+ uint getType() const {
return _type;
}
protected:
- virtual ~Resource() {};
+ virtual ~Resource() {}
private:
- Common::String _fileName; ///< The absolute filename
- uint _fileNameHash; ///< The hash value of the filename
- uint _refCount; ///< The number of locks
- uint _type; ///< The type of the resource
+ Common::String _fileName; ///< The absolute filename
+ uint _refCount; ///< The number of locks
+ uint _type; ///< The type of the resource
Common::List<Resource *>::iterator _iterator; ///< Points to the resource position in the LRU list
};
diff --git a/engines/sword25/kernel/resservice.h b/engines/sword25/kernel/resservice.h
index d5961d52ae..a0f2669231 100644
--- a/engines/sword25/kernel/resservice.h
+++ b/engines/sword25/kernel/resservice.h
@@ -35,7 +35,6 @@
#ifndef SWORD25_RESOURCESERVICE_H
#define SWORD25_RESOURCESERVICE_H
-// Includes
#include "sword25/kernel/common.h"
#include "sword25/kernel/service.h"
#include "sword25/kernel/kernel.h"
@@ -48,8 +47,8 @@ class Resource;
class ResourceService : public Service {
public:
ResourceService(Kernel *pKernel) : Service(pKernel) {
- ResourceManager *pResource = pKernel->GetResourceManager();
- pResource->RegisterResourceService(this);
+ ResourceManager *pResource = pKernel->getResourceManager();
+ pResource->registerResourceService(this);
}
virtual ~ResourceService() {}
diff --git a/engines/sword25/kernel/scummvmwindow.cpp b/engines/sword25/kernel/scummvmwindow.cpp
deleted file mode 100644
index 35fd27a05c..0000000000
--- a/engines/sword25/kernel/scummvmwindow.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-#include "common/system.h"
-#include "engines/util.h"
-#include "graphics/pixelformat.h"
-#include "sword25/kernel/scummvmwindow.h"
-#include "sword25/kernel/kernel.h"
-#include "sword25/input/inputengine.h"
-
-#define BS_LOG_PREFIX "WIN32WINDOW"
-
-namespace Sword25 {
-
-bool ScummVMWindow::_ClassRegistered = false;
-
-// Constructor / Destructor
-// ------------------------
-ScummVMWindow::ScummVMWindow(int X, int Y, int Width, int Height, bool Visible) {
- // Presume that init will fail
- _InitSuccess = false;
-
- // We don't support any window creation except at the origin 0,0
- assert(X == 0);
- assert(Y == 0);
-
- if (!_ClassRegistered) {
- // Nothing here currently
-
- _ClassRegistered = true;
- }
-
- // Fenstersichtbarkeit setzen
- SetVisible(Visible);
-
- // Indicate success
- _InitSuccess = true;
- _WindowAlive = true;
- _CloseWanted = false;
-}
-
-ScummVMWindow::~ScummVMWindow() {
-}
-
-// Get Methods
-// ------------
-int ScummVMWindow::GetX() {
- return 0;
-}
-
-int ScummVMWindow::GetY() {
- return 0;
-}
-
-int ScummVMWindow::GetClientX() {
- return 0;
-}
-
-int ScummVMWindow::GetClientY() {
- return 0;
-}
-
-int ScummVMWindow::GetWidth() {
- return g_system->getWidth();
-}
-
-int ScummVMWindow::GetHeight() {
- return g_system->getHeight();
-}
-
-Common::String ScummVMWindow::GetTitle() {
- return Common::String("");
-}
-
-bool ScummVMWindow::IsVisible() {
- return true;
-}
-
-bool ScummVMWindow::HasFocus() {
- // FIXME: Is there a way to tell if ScummVM has the focus in Windowed mode?
- return true;
-}
-
-uint ScummVMWindow::GetWindowHandle() {
- return 0;
-}
-
-void ScummVMWindow::SetWindowAlive(bool v) {
- _WindowAlive = v;
-}
-
-
-// Set Methods
-// ------------
-
-void ScummVMWindow::SetX(int X) {
- // No implementation
-}
-
-void ScummVMWindow::SetY(int Y) {
- // No implementation
-}
-
-void ScummVMWindow::SetWidth(int Width) {
- // No implementation
-}
-
-void ScummVMWindow::SetHeight(int Height) {
- // No implementation
-}
-
-void ScummVMWindow::SetVisible(bool Visible) {
- // No implementation
-}
-
-void ScummVMWindow::SetTitle(const Common::String &Title) {
- // No implementation
-}
-
-bool ScummVMWindow::ProcessMessages() {
- // All messages are handled separately in the input manager. The only thing we
- // need to do here is to keep returning whether the window/game is still alive
- return _WindowAlive;
-}
-
-bool ScummVMWindow::WaitForFocus() {
- // No implementation
- return true;
-}
-
-// FIXME: Special keys detected here need to be moved into the Input Engine
-/*
-// Die WindowProc aller Fenster der Klasse
-LRESULT CALLBACK BS_ScummVMWindow::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- switch(uMsg)
- {
- case WM_PAINT:
- ValidateRect(hwnd, NULL);
- break;
-
- case WM_DESTROY:
- // Das Fenster wird zerstört
- PostQuitMessage(0);
- break;
-
- case WM_CLOSE:
- {
- BS_Window * WindowPtr = BS_Kernel::GetInstance()->GetWindow();
- if (WindowPtr) {
- WindowPtr->SetCloseWanted(true);
- }
- break;
- }
-
- case WM_KEYDOWN:
- {
- // Tastendrücke, die für das Inputmodul interessant sind, werden diesem gemeldet.
- BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput();
-
- if (InputPtr)
- {
- switch (wParam)
- {
- case VK_RETURN:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_ENTER);
- break;
-
- case VK_LEFT:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_LEFT);
- break;
-
- case VK_RIGHT:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_RIGHT);
- break;
-
- case VK_HOME:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_HOME);
- break;
-
- case VK_END:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_END);
- break;
-
- case VK_BACK:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_BACKSPACE);
- break;
-
- case VK_TAB:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_TAB);
- break;
-
- case VK_INSERT:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_INSERT);
- break;
-
- case VK_DELETE:
- InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_DELETE);
- break;
- }
- }
- break;
- }
-
- case WM_KEYUP:
- case WM_SYSKEYUP:
- // Alle Tastendrücke werden ignoriert, damit Windows per DefWindowProc() nicht darauf
- // reagieren kann und damit unerwartete Seiteneffekte auslöst.
- // Zum Beispiel würden ALT und F10 Tastendrücke das "Menü" aktivieren und somit den Message-Loop zum Stillstand bringen.
- break;
-
- case WM_SYSCOMMAND:
- // Verhindern, dass der Bildschirmschoner aktiviert wird, während das Spiel läuft
- if (wParam != SC_SCREENSAVE) return DefWindowProc(hwnd,uMsg,wParam,lParam);
- break;
-
- case WM_CHAR:
- {
- byte theChar = static_cast<byte>(wParam & 0xff);
-
- // Alle Zeichen, die keine Steuerzeichen sind, werden als Buchstaben dem Input-Service mitgeteilt.
- if (theChar >= 32)
- {
- BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput();
- if (InputPtr) InputPtr->ReportCharacter(theChar);
- }
- }
- break;
-
- case WM_SETCURSOR:
- {
- // Der Systemcursor wird in der Client-Area des Fensters nicht angezeigt, jedoch in der nicht Client-Area, damit der Benutzer das Fenster wie gewohnt
- // schließen und verschieben kann.
-
- // Koordinaten des Cursors in der Client-Area berechnen.
- POINT pt;
- GetCursorPos(&pt);
- ScreenToClient(hwnd, &pt);
-
- // Feststellen, ob sich der Cursor in der Client-Area befindet.
- // Get client rect
- RECT rc;
- GetClientRect(hwnd, &rc);
-
- // See if cursor is in client area
- if(PtInRect(&rc, pt))
- // In der Client-Area keinen Cursor anzeigen.
- SetCursor(NULL);
- else
- // Ausserhalb der Client-Area den Cursor anzeigen.
- SetCursor(LoadCursor(NULL, IDC_ARROW));
-
- return TRUE;
- }
- break;
-
- default:
- // Um alle anderen Vorkommnisse kümmert sich Windows
- return DefWindowProc(hwnd,uMsg,wParam,lParam);
- }
-
- return 0;
-}
-*/
-
-} // End of namespace Sword25
diff --git a/engines/sword25/kernel/scummvmwindow.h b/engines/sword25/kernel/scummvmwindow.h
deleted file mode 100644
index 2b5f514b7d..0000000000
--- a/engines/sword25/kernel/scummvmwindow.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-/*
- BS_ScummVMWindow
- ----------------
- Implementation of the BS_Window Interfaces for ScummVM
-*/
-
-#ifndef SWORD25_SCUMMVMWINDOW_H
-#define SWORD25_SCUMMVMWINDOW_H
-
-// Includes
-#include "sword25/kernel/common.h"
-#include "sword25/kernel/window.h"
-
-namespace Sword25 {
-
-// Class definition
-class ScummVMWindow : public Window {
-public:
- ScummVMWindow(int X, int Y, int Width, int Height, bool Visible);
- virtual ~ScummVMWindow();
-
- bool IsVisible();
- void SetVisible(bool Visible);
- int GetX();
- void SetX(int X);
- int GetY();
- void SetY(int X);
- int GetClientX();
- int GetClientY();
- int GetWidth();
- void SetWidth(int Width);
- int GetHeight();
- void SetHeight(int Height);
- Common::String GetTitle();
- void SetWindowAlive(bool v);
- void SetTitle(const Common::String &Title);
- bool HasFocus();
- uint GetWindowHandle();
- bool WaitForFocus();
- bool ProcessMessages();
-
-private:
- static bool _ClassRegistered;
- bool _WindowAlive;
- int _ClientXDelta;
- int _ClientYDelta;
-};
-
-} // End of namespace Sword25
-
-#endif
diff --git a/engines/sword25/kernel/service.h b/engines/sword25/kernel/service.h
index addcf50a08..ef8858bb7d 100644
--- a/engines/sword25/kernel/service.h
+++ b/engines/sword25/kernel/service.h
@@ -60,14 +60,14 @@ private:
Kernel *_pKernel;
protected:
- Service(Kernel *pKernel) : _pKernel(pKernel) {};
+ Service(Kernel *pKernel) : _pKernel(pKernel) {}
Kernel *GetKernel() const {
return _pKernel;
}
public:
- virtual ~Service() {};
+ virtual ~Service() {}
};
} // End of namespace Sword25
diff --git a/engines/sword25/kernel/service_ids.h b/engines/sword25/kernel/service_ids.h
deleted file mode 100644
index 5ffd83d743..0000000000
--- a/engines/sword25/kernel/service_ids.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-/*
- * service_ids.h
- * -------------
- * This file lists all the services.
- * EVERY new service needs to be entered here, otherwise it cannot be instantiated
- * by pKernel->NewService(..)
- *
- * Autor: Malte Thiesen
- */
-
-#ifndef SWORD25_SERVICE_IDS
-#define SWORD25_SERVICE_IDS
-
-#include "sword25/kernel/common.h"
-
-namespace Sword25 {
-
-Service *GraphicEngine_CreateObject(Kernel *pKernel);
-Service *PackageManager_CreateObject(Kernel *pKernel);
-Service *InputEngine_CreateObject(Kernel *pKernel);
-Service *SoundEngine_CreateObject(Kernel *pKernel);
-Service *LuaScriptEngine_CreateObject(Kernel *pKernel);
-Service *Geometry_CreateObject(Kernel *pKernel);
-Service *OggTheora_CreateObject(Kernel *pKernel);
-
-// Services are recorded in this table
-const BS_ServiceInfo BS_SERVICE_TABLE[] = {
- // The first two parameters are the name of the superclass and service
- // The third parameter is the static method of the class that creates an object
- // of the class and returns it
- // Example:
- // BS_ServiceInfo("Superclass", "Service", CreateMethod)
- BS_ServiceInfo("gfx", "opengl", GraphicEngine_CreateObject),
- BS_ServiceInfo("package", "archiveFS", PackageManager_CreateObject),
- BS_ServiceInfo("input", "winapi", InputEngine_CreateObject),
- BS_ServiceInfo("sfx", "fmodex", SoundEngine_CreateObject),
- BS_ServiceInfo("script", "lua", LuaScriptEngine_CreateObject),
- BS_ServiceInfo("geometry", "std", Geometry_CreateObject),
- BS_ServiceInfo("fmv", "oggtheora", OggTheora_CreateObject),
-};
-
-const uint BS_SERVICE_COUNT = sizeof(BS_SERVICE_TABLE) / sizeof(BS_ServiceInfo);
-
-} // End of namespace Sword25
-
-#endif
diff --git a/engines/sword25/kernel/string.h b/engines/sword25/kernel/string.h
deleted file mode 100644
index b701e2312b..0000000000
--- a/engines/sword25/kernel/string.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-#ifndef SWORD25_STRING
-#define SWORD25_STRING
-
-#include "common/str.h"
-
-namespace BS_String {
-
-inline uint GetHash(const Common::String &Str) {
- uint Result = 0;
-
- for (uint i = 0; i < Str.size(); i++)
- Result = ((Result << 5) - Result) + Str[i];
-
- return Result;
-}
-
-inline bool ToInt(const Common::String &Str, int &Result) {
- Common::String::const_iterator Iter = Str.begin();
-
- // Skip whitespaces
- while (*Iter && (*Iter == ' ' || *Iter == '\t')) {
- ++Iter;
- }
- if (Iter == Str.end()) return false;
-
- // Read sign, if available
- bool IsNegative = false;
- if (*Iter == '-') {
- IsNegative = true;
- ++Iter;
- } else if (*Iter == '+')
- ++Iter;
-
- // Skip whitespaces
- while (*Iter && (*Iter == ' ' || *Iter == '\t')) {
- ++Iter;
- }
- if (Iter == Str.end()) return false;
-
- // Convert string to integer
- Result = 0;
- while (Iter != Str.end()) {
- if (*Iter < '0' || *Iter > '9') {
- while (*Iter && (*Iter == ' ' || *Iter == '\t')) {
- ++Iter;
- }
- if (Iter != Str.end()) return false;
- break;
- }
- Result = (Result * 10) + (*Iter - '0');
- ++Iter;
- }
-
- if (IsNegative) Result = -Result;
-
- return true;
-}
-
-inline bool ToBool(const Common::String &Str, bool &Result) {
- if (Str == "true" || Str == "TRUE") {
- Result = true;
- return true;
- } else if (Str == "false" || Str == "FALSE") {
- Result = false;
- return true;
- }
-
- return false;
-}
-
-inline void ToLower(Common::String &Str) {
- Str.toLowercase();
-}
-
-} // End of namespace BS_String
-
-#endif
diff --git a/engines/sword25/kernel/window.cpp b/engines/sword25/kernel/window.cpp
deleted file mode 100644
index 8d2dc309e7..0000000000
--- a/engines/sword25/kernel/window.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-#include "sword25/kernel/window.h"
-
-// Alle Implementationen von BS_Window müssen hier eingetragen werden
-#include "sword25/kernel/scummvmwindow.h"
-
-namespace Sword25 {
-
-// Erstellt ein Fenster des GUI des aktuellen Betriebssystems
-Window *Window::CreateBSWindow(int X, int Y, int Width, int Height, bool Visible) {
- // Fenster erstellen
- Window *pWindow = (Window *) new ScummVMWindow(X, Y, Width, Height, Visible);
-
- // Falls das Fenster erfolgreich initialisiert wurde, wird ein Pointer auf das Fensterobjekt
- // zurückgegeben
- if (pWindow->_InitSuccess)
- return pWindow;
-
- // Ansonsten wird das Fensterobjekt zerstört und NULL zurückgegeben
- delete pWindow;
- return NULL;
-}
-
-// Gibt True zurück wenn das Fenster WM_CLOSE empfangen hat -
-// solange, bis RejectClose() aufgerufen wurde.
-bool Window::CloseWanted() {
- bool result = _CloseWanted;
- _CloseWanted = false;
- return result;
-}
-
-void Window::SetCloseWanted(bool Wanted) {
- _CloseWanted = Wanted;
-}
-
-} // End of namespace Sword25
diff --git a/engines/sword25/kernel/window.h b/engines/sword25/kernel/window.h
deleted file mode 100644
index aee23087cb..0000000000
--- a/engines/sword25/kernel/window.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/*
- * This code is based on Broken Sword 2.5 engine
- *
- * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
- *
- * Licensed under GNU GPL v2
- *
- */
-
-/*
- * BS_Window
- * ---------
- * Simple window class interface. This is being encapsulated in a class for
- * reasons of portability.
- *
- * Autor: Malte Thiesen
- */
-
-#ifndef SWORD25_WINDOW_H
-#define SWORD25_WINDOW_H
-
-// Includes
-#include "sword25/kernel/common.h"
-
-namespace Sword25 {
-
-// Class definitions
-
-/**
- * A simple window class interface
- *
- * Windows are exclusively created by BS_Window::CreateBSWindow().
- * BS_Windows selects the correct class for the environment.
- */
-class Window {
-protected:
- bool _InitSuccess;
- bool _CloseWanted;
-
-public:
- virtual ~Window() {};
-
- /**
- * Returns the visibility of the window.
- */
- virtual bool IsVisible() = 0;
-
- /**
- * Sets the visibility of the window
- * @param Visible Specifies whether the window should be visible or hidden
- */
- virtual void SetVisible(bool Visible) = 0;
- /**
- * Returns the X position of the window
- */
- virtual int GetX() = 0;
- /**
- * Sets the X position of the window
- * @paramX The new X position for the window, or -1 for centre aligned
- */
- virtual void SetX(int X) = 0;
- /**
- * Gets the Y position of the window
- */
- virtual int GetY() = 0;
- /**
- * Sets the Y position of the window
- * @param Y The new Y position for the window, or -1 for centre aligned
- */
- virtual void SetY(int X) = 0;
- /**
- * Returns the X position of the window's client area
- */
- virtual int GetClientX() = 0;
- /**
- * Returns the Y position of the window's client area
- */
- virtual int GetClientY() = 0;
- /**
- * Returns the width of the window without the frame
- */
- virtual int GetWidth() = 0;
- /**
- * Sets the width of the window without the frame
- */
- virtual void SetWidth(int Width) = 0;
- /**
- * Gets the height of the window without the frame
- */
- virtual int GetHeight() = 0;
- /**
- * Sets the height of the window without the frame
- */
- virtual void SetHeight(int Height) = 0;
- /**
- * Returns the title of the window
- */
- virtual Common::String GetTitle() = 0;
- /**
- * Sets the title of the window
- * @param Title The new window title
- */
- virtual void SetTitle(const Common::String &Title) = 0;
- /**
- * Handle the processing of any pending window messages. This method should be called
- * during the main loop.
- */
- virtual bool ProcessMessages() = 0;
- /**
- * Pauses the applicaiton until the window has focus, or has been closed.
- * Returns false if the window was closed.
- */
- virtual bool WaitForFocus() = 0;
- /**
- * Returns true if the window has focus, false otherwise.
- */
- virtual bool HasFocus() = 0;
- /**
- * Returns the system handle that represents the window. Note that any use of the handle
- * will not be portable code.
- */
- virtual uint GetWindowHandle() = 0;
-
- virtual void SetWindowAlive(bool v) = 0;
-
- /**
- * Specifies whether the window is wanted to be closed. This is used together with CloseWanted()
- * to allow scripts to query when the main window should be closed, or the user is asking it to close
- **/
- void SetCloseWanted(bool Wanted);
- /**
- * Returns the previous value set in a call to SetCloseWanted.
- * Note that calling this also resets the value back to false, until such time as the SetCloseWanted()
- * method is called again.
- **/
- bool CloseWanted();
-
- /**
- * Creates a new window instance. Returns a pointer to the window, or NULL if the creation failed.
- * Note: It is the responsibility of the client to free the pointer when done with it.
- * @param X The X position of the window, or -1 for centre horizontal alignment
- * @param Y The Y position of the window, or -1 for centre vertical alignment
- * @param Width The width of the window without the frame
- * @param Height The height of the window without the frame
- * @param Visible Specifies whether window should be visible
- */
- static Window *CreateBSWindow(int X, int Y, int Width, int Height, bool Visible);
-};
-
-} // End of namespace Sword25
-
-#endif
diff --git a/engines/sword25/math/geometry.cpp b/engines/sword25/math/geometry.cpp
index caa10160e9..bad6fcdb06 100644
--- a/engines/sword25/math/geometry.cpp
+++ b/engines/sword25/math/geometry.cpp
@@ -46,8 +46,4 @@ Geometry::Geometry(Kernel *pKernel) : Service(pKernel) {
}
-Service *Geometry_CreateObject(Kernel *pKernel) {
- return new Geometry(pKernel);
-}
-
} // End of namespace Sword25
diff --git a/engines/sword25/math/geometry_script.cpp b/engines/sword25/math/geometry_script.cpp
index dac766927b..8882d5e588 100644
--- a/engines/sword25/math/geometry_script.cpp
+++ b/engines/sword25/math/geometry_script.cpp
@@ -32,10 +32,6 @@
*
*/
-// -----------------------------------------------------------------------------
-// Includes
-// -----------------------------------------------------------------------------
-
#include "common/array.h"
#include "sword25/gfx/graphicengine.h"
#include "sword25/kernel/common.h"
@@ -205,7 +201,7 @@ static uint tableRegionToRegion(lua_State *L, const char *className) {
case LUA_TNUMBER: {
Polygon polygon;
tablePolygonToPolygon(L, polygon);
- RegionRegistry::getInstance().resolveHandle(regionHandle)->init(polygon);
+ RegionRegistry::instance().resolveHandle(regionHandle)->init(polygon);
}
break;
@@ -217,7 +213,7 @@ static uint tableRegionToRegion(lua_State *L, const char *className) {
int polygonCount = luaL_getn(L, -1);
if (polygonCount == 1)
- RegionRegistry::getInstance().resolveHandle(regionHandle)->init(polygon);
+ RegionRegistry::instance().resolveHandle(regionHandle)->init(polygon);
else {
Common::Array<Polygon> holes;
holes.reserve(polygonCount - 1);
@@ -230,7 +226,7 @@ static uint tableRegionToRegion(lua_State *L, const char *className) {
}
BS_ASSERT((int)holes.size() == polygonCount - 1);
- RegionRegistry::getInstance().resolveHandle(regionHandle)->init(polygon, &holes);
+ RegionRegistry::instance().resolveHandle(regionHandle)->init(polygon, &holes);
}
}
break;
@@ -283,7 +279,7 @@ static Region *checkRegion(lua_State *L) {
uint *regionHandlePtr;
if ((regionHandlePtr = reinterpret_cast<uint *>(my_checkudata(L, 1, REGION_CLASS_NAME))) != 0 ||
(regionHandlePtr = reinterpret_cast<uint *>(my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0) {
- return RegionRegistry::getInstance().resolveHandle(*regionHandlePtr);
+ return RegionRegistry::instance().resolveHandle(*regionHandlePtr);
} else {
luaL_argcheck(L, 0, 1, "'" REGION_CLASS_NAME "' expected");
}
@@ -364,13 +360,13 @@ static int r_setY(lua_State *L) {
}
static void drawPolygon(const Polygon &polygon, uint color, const Vertex &offset) {
- GraphicEngine *pGE = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ GraphicEngine *pGE = Kernel::getInstance()->getGfx();
BS_ASSERT(pGE);
for (int i = 0; i < polygon.vertexCount - 1; i++)
- pGE->DrawDebugLine(polygon.vertices[i] + offset, polygon.vertices[i + 1] + offset, color);
+ pGE->drawDebugLine(polygon.vertices[i] + offset, polygon.vertices[i + 1] + offset, color);
- pGE->DrawDebugLine(polygon.vertices[polygon.vertexCount - 1] + offset, polygon.vertices[0] + offset, color);
+ pGE->drawDebugLine(polygon.vertices[polygon.vertexCount - 1] + offset, polygon.vertices[0] + offset, color);
}
static void drawRegion(const Region &region, uint color, const Vertex &offset) {
@@ -387,12 +383,12 @@ static int r_draw(lua_State *L) {
case 3: {
Vertex offset;
Vertex::luaVertexToVertex(L, 3, offset);
- drawRegion(*pR, GraphicEngine::LuaColorToARGBColor(L, 2), offset);
+ drawRegion(*pR, GraphicEngine::luaColorToARGBColor(L, 2), offset);
}
break;
case 2:
- drawRegion(*pR, GraphicEngine::LuaColorToARGBColor(L, 2), Vertex(0, 0));
+ drawRegion(*pR, GraphicEngine::luaColorToARGBColor(L, 2), Vertex(0, 0));
break;
default:
@@ -436,7 +432,7 @@ static WalkRegion *checkWalkRegion(lua_State *L) {
// The first parameter must be of type 'userdate', and the Metatable class Geo.WalkRegion
uint regionHandle;
if ((regionHandle = *reinterpret_cast<uint *>(my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0) {
- return reinterpret_cast<WalkRegion *>(RegionRegistry::getInstance().resolveHandle(regionHandle));
+ return reinterpret_cast<WalkRegion *>(RegionRegistry::instance().resolveHandle(regionHandle));
} else {
luaL_argcheck(L, 0, 1, "'" WALKREGION_CLASS_NAME "' expected");
}
@@ -474,9 +470,9 @@ static const luaL_reg WALKREGION_METHODS[] = {
};
bool Geometry::registerScriptBindings() {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pScript = pKernel->getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast< lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
diff --git a/engines/sword25/math/polygon.cpp b/engines/sword25/math/polygon.cpp
index 700c375e8c..a83a8aa1dd 100644
--- a/engines/sword25/math/polygon.cpp
+++ b/engines/sword25/math/polygon.cpp
@@ -49,7 +49,7 @@ Polygon::Polygon(int vertexCount_, const Vertex *vertices_) : vertexCount(0), ve
init(vertexCount_, vertices_);
}
-Polygon::Polygon(const Polygon &other) : vertexCount(0), vertices(NULL) {
+Polygon::Polygon(const Polygon &other) : Persistable(other), vertexCount(0), vertices(NULL) {
init(other.vertexCount, other.vertices);
}
diff --git a/engines/sword25/math/polygon.h b/engines/sword25/math/polygon.h
index 3bd243f666..1b2a0b191c 100644
--- a/engines/sword25/math/polygon.h
+++ b/engines/sword25/math/polygon.h
@@ -193,6 +193,11 @@ public:
virtual bool persist(OutputPersistenceBlock &writer);
virtual bool unpersist(InputPersistenceBlock &reader);
+ Polygon &operator=(const Polygon &p) {
+ init(p.vertexCount, p.vertices);
+ return *this;
+ }
+
private:
bool _isCW;
bool _isConvex;
diff --git a/engines/sword25/math/region.cpp b/engines/sword25/math/region.cpp
index 454f90a8ba..ae9b76d077 100644
--- a/engines/sword25/math/region.cpp
+++ b/engines/sword25/math/region.cpp
@@ -44,11 +44,11 @@
namespace Sword25 {
Region::Region() : _valid(false), _type(RT_REGION) {
- RegionRegistry::getInstance().registerObject(this);
+ RegionRegistry::instance().registerObject(this);
}
Region::Region(InputPersistenceBlock &reader, uint handle) : _valid(false), _type(RT_REGION) {
- RegionRegistry::getInstance().registerObject(this, handle);
+ RegionRegistry::instance().registerObject(this, handle);
unpersist(reader);
}
@@ -67,7 +67,7 @@ uint Region::create(REGION_TYPE type) {
BS_ASSERT(true);
}
- return RegionRegistry::getInstance().resolvePtr(regionPtr);
+ return RegionRegistry::instance().resolvePtr(regionPtr);
}
uint Region::create(InputPersistenceBlock &reader, uint handle) {
@@ -85,11 +85,11 @@ uint Region::create(InputPersistenceBlock &reader, uint handle) {
BS_ASSERT(false);
}
- return RegionRegistry::getInstance().resolvePtr(regionPtr);
+ return RegionRegistry::instance().resolvePtr(regionPtr);
}
Region::~Region() {
- RegionRegistry::getInstance().deregisterObject(this);
+ RegionRegistry::instance().deregisterObject(this);
}
bool Region::init(const Polygon &contour, const Common::Array<Polygon> *pHoles) {
diff --git a/engines/sword25/math/regionregistry.cpp b/engines/sword25/math/regionregistry.cpp
index 1509ea9e5e..c1fd6e98a8 100644
--- a/engines/sword25/math/regionregistry.cpp
+++ b/engines/sword25/math/regionregistry.cpp
@@ -39,9 +39,9 @@
#include "sword25/math/regionregistry.h"
#include "sword25/math/region.h"
-namespace Sword25 {
+DECLARE_SINGLETON(Sword25::RegionRegistry);
-Common::SharedPtr<RegionRegistry> RegionRegistry::_instancePtr;
+namespace Sword25 {
void RegionRegistry::logErrorLn(const char *message) const {
BS_LOG_ERRORLN(message);
diff --git a/engines/sword25/math/regionregistry.h b/engines/sword25/math/regionregistry.h
index bbe2fb8370..560d4ae4a9 100644
--- a/engines/sword25/math/regionregistry.h
+++ b/engines/sword25/math/regionregistry.h
@@ -35,7 +35,8 @@
#ifndef SWORD25_REGIONREGISTRY_H
#define SWORD25_REGIONREGISTRY_H
-#include "common/ptr.h"
+#include "common/singleton.h"
+
#include "sword25/kernel/common.h"
#include "sword25/kernel/persistable.h"
#include "sword25/kernel/objectregistry.h"
@@ -44,21 +45,17 @@ namespace Sword25 {
class Region;
-class RegionRegistry : public ObjectRegistry<Region>, public Persistable {
+class RegionRegistry :
+ public ObjectRegistry<Region>,
+ public Persistable,
+ public Common::Singleton<RegionRegistry> {
public:
- static RegionRegistry &getInstance() {
- if (!_instancePtr.get()) _instancePtr = Common::SharedPtr<RegionRegistry>(new RegionRegistry());
- return *_instancePtr.get();
- }
-
virtual bool persist(OutputPersistenceBlock &writer);
virtual bool unpersist(InputPersistenceBlock &reader);
private:
virtual void logErrorLn(const char *message) const;
virtual void logWarningLn(const char *message) const;
-
- static Common::SharedPtr<RegionRegistry> _instancePtr;
};
} // End of namespace Sword25
diff --git a/engines/sword25/math/vertex.cpp b/engines/sword25/math/vertex.cpp
index 4997da09d3..d9a709ab49 100644
--- a/engines/sword25/math/vertex.cpp
+++ b/engines/sword25/math/vertex.cpp
@@ -34,15 +34,8 @@
#include "sword25/math/vertex.h"
-namespace Lua {
-
-extern "C"
-{
#include "sword25/util/lua/lua.h"
#include "sword25/util/lua/lauxlib.h"
-}
-
-}
namespace Sword25 {
diff --git a/engines/sword25/math/vertex.h b/engines/sword25/math/vertex.h
index 87e4694d48..b923841a0f 100644
--- a/engines/sword25/math/vertex.h
+++ b/engines/sword25/math/vertex.h
@@ -45,15 +45,24 @@
// Includes
#include <math.h>
#include "sword25/kernel/common.h"
-
-namespace Lua {
-
-// Forward declarations
-struct lua_State;
-
-}
-
-using namespace Lua;
+#include "sword25/util/lua/lua.h"
+
+#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
+// Older versions of Mac OS X didn't supply a powf function, so using it
+// will cause a binary incompatibility when trying to run a binary built
+// on a newer OS X release on an olderr one. And Solaris 8 doesn't provide
+// powf, floorf, fabsf etc. at all.
+// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
+// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
+// powf, resulting in a linker error because of multiple definitions.
+// Hence we re-define them here. The only potential drawback is that it
+// might be a little bit slower this way.
+#define powf(x,y) ((float)pow(x,y))
+#define floorf(x) ((float)floor(x))
+#define fabsf(x) ((float)fabs(x))
+#define sqrtf(x) ((float)sqrt(x))
+#define atan2f(x,y) ((float)atan2(x,y))
+#endif
namespace Sword25 {
@@ -62,7 +71,7 @@ namespace Sword25 {
*/
class Vertex {
public:
- Vertex() : x(0), y(0) {};
+ Vertex() : x(0), y(0) {}
Vertex(int x_, int y_) {
this->x = x_;
this->y = y_;
diff --git a/engines/sword25/math/walkregion.cpp b/engines/sword25/math/walkregion.cpp
index 7cdd8c64c5..51818bc9e8 100644
--- a/engines/sword25/math/walkregion.cpp
+++ b/engines/sword25/math/walkregion.cpp
@@ -95,7 +95,7 @@ struct DijkstraNode {
typedef Container::iterator Iter;
typedef Container::const_iterator ConstIter;
- DijkstraNode() : cost(Infinity), chosen(false) {};
+ DijkstraNode() : cost(Infinity), chosen(false) {}
ConstIter parentIter;
int cost;
bool chosen;
@@ -164,6 +164,17 @@ static void relaxEndPoint(const Vertex &curNodePos,
}
}
+template<class T>
+void reverseArray(Common::Array<T> &arr) {
+ const uint size = arr.size();
+ if (size < 2)
+ return;
+
+ for (uint i = 0; i <= (size / 2 - 1); ++i) {
+ SWAP(arr[i], arr[size - i - 1]);
+ }
+}
+
bool WalkRegion::findPath(const Vertex &start, const Vertex &end, BS_Path &path) const {
// This is an implementation of Dijkstra's algorithm
@@ -205,7 +216,7 @@ bool WalkRegion::findPath(const Vertex &start, const Vertex &end, BS_Path &path)
// The nodes of the path must be untwisted, as they were extracted in reverse order.
// This step could be saved if the path from end to the beginning was desired
- ReverseArray<Vertex>(path);
+ reverseArray<Vertex>(path);
return true;
}
diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk
index ebe50976af..944e2c49c3 100644
--- a/engines/sword25/module.mk
+++ b/engines/sword25/module.mk
@@ -1,12 +1,11 @@
MODULE := engines/sword25
MODULE_OBJS := \
+ console.o \
detection.o \
sword25.o \
fmv/movieplayer.o \
fmv/movieplayer_script.o \
- fmv/theora_decoder.o \
- fmv/yuvtorgba.o \
gfx/animation.o \
gfx/animationdescription.o \
gfx/animationresource.o \
@@ -28,8 +27,6 @@ MODULE_OBJS := \
gfx/text.o \
gfx/timedrenderobject.o \
gfx/image/art.o \
- gfx/image/b25sloader.o \
- gfx/image/imageloader.o \
gfx/image/pngloader.o \
gfx/image/renderedimage.o \
gfx/image/swimage.o \
@@ -37,7 +34,6 @@ MODULE_OBJS := \
gfx/image/vectorimagerenderer.o \
input/inputengine.o \
input/inputengine_script.o \
- kernel/callbackregistry.o \
kernel/filesystemutil.o \
kernel/inputpersistenceblock.o \
kernel/kernel.o \
@@ -47,8 +43,6 @@ MODULE_OBJS := \
kernel/persistenceservice.o \
kernel/resmanager.o \
kernel/resource.o \
- kernel/scummvmwindow.o \
- kernel/window.o \
math/geometry.o \
math/geometry_script.o \
math/polygon.o \
@@ -90,8 +84,6 @@ MODULE_OBJS := \
util/lua/ltable.o \
util/lua/ltablib.o \
util/lua/ltm.o \
- util/lua/lua.o \
- util/lua/luac.o \
util/lua/lundump.o \
util/lua/lvm.o \
util/lua/lzio.o \
@@ -100,6 +92,12 @@ MODULE_OBJS := \
util/pluto/pluto.o \
util/pluto/plzio.o
+ifdef USE_THEORADEC
+MODULE_OBJS += \
+ fmv/theora_decoder.o \
+ fmv/yuvtorgba.o
+endif
+
# This module can be built as a plugin
ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN)
PLUGIN := 1
diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp
index 063844f3ba..4765d26ed4 100644
--- a/engines/sword25/package/packagemanager.cpp
+++ b/engines/sword25/package/packagemanager.cpp
@@ -75,10 +75,6 @@ PackageManager::~PackageManager() {
}
-Service *PackageManager_CreateObject(Kernel *kernelPtr) {
- return new PackageManager(kernelPtr);
-}
-
/**
* Scans through the archive list for a specified file
*/
@@ -154,13 +150,13 @@ byte *PackageManager::getFile(const Common::String &fileName, uint *fileSizePtr)
// Savegame loading logic
Common::SaveFileManager *sfm = g_system->getSavefileManager();
Common::InSaveFile *file = sfm->openForLoading(
- FileSystemUtil::GetInstance().GetPathFilename(fileName));
+ FileSystemUtil::getPathFilename(fileName));
if (!file) {
BS_LOG_ERRORLN("Could not load savegame \"%s\".", fileName.c_str());
return 0;
}
- if (*fileSizePtr)
+ if (fileSizePtr)
*fileSizePtr = file->size();
byte *buffer = new byte[file->size()];
@@ -186,7 +182,7 @@ byte *PackageManager::getFile(const Common::String &fileName, uint *fileSizePtr)
delete in;
if (!bytesRead) {
- delete buffer;
+ delete[] buffer;
return NULL;
}
@@ -218,27 +214,14 @@ Common::String PackageManager::getAbsolutePath(const Common::String &fileName) {
return normalizePath(fileName, _currentDirectory);
}
-uint PackageManager::getFileSize(const Common::String &fileName) {
- Common::SeekableReadStream *in;
- Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
- if (!fileNode)
- return 0;
- if (!(in = fileNode->createReadStream()))
- return 0;
-
- uint fileSize = in->size();
-
- return fileSize;
-}
-
-uint PackageManager::getFileType(const Common::String &fileName) {
- warning("STUB: BS_PackageManager::GetFileType(%s)", fileName.c_str());
-
- //return fileNode.isDirectory() ? BS_PackageManager::FT_DIRECTORY : BS_PackageManager::FT_FILE;
- return PackageManager::FT_FILE;
-}
-
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") {
+ // To get around this, change to detecting one of the files in the folder
+ return getArchiveMember(normalizePath(fileName + "/APO0001.ogg", _currentDirectory));
+ }
+
Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
return fileNode;
}
diff --git a/engines/sword25/package/packagemanager.h b/engines/sword25/package/packagemanager.h
index 96f136dd83..03598012a6 100644
--- a/engines/sword25/package/packagemanager.h
+++ b/engines/sword25/package/packagemanager.h
@@ -190,23 +190,6 @@ public:
int doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter = FT_DIRECTORY | FT_FILE);
/**
- * Returns a file's size
- * @param FileName The filename
- * @return The file size. If an error occurs, then 0xffffffff is returned.
- * @remarks For files in packages, then uncompressed size is returned.
- **/
- uint getFileSize(const Common::String &fileName);
-
- /**
- * Returns the type of a file.
- * @param FileName The filename
- * @return Returns the file type, either (BS_PackageManager::FT_DIRECTORY
- * or BS_PackageManager::FT_FILE).
- * If the file was not found, then 0 is returned.
- */
- uint getFileType(const Common::String &fileName);
-
- /**
* Determines whether a file exists
* @param FileName The filename
* @return Returns true if the file exists, otherwise false.
diff --git a/engines/sword25/package/packagemanager_script.cpp b/engines/sword25/package/packagemanager_script.cpp
index cfcea55944..9367ae3071 100644
--- a/engines/sword25/package/packagemanager_script.cpp
+++ b/engines/sword25/package/packagemanager_script.cpp
@@ -41,12 +41,10 @@
namespace Sword25 {
-using namespace Lua;
-
static PackageManager *getPM() {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- PackageManager *pPM = static_cast<PackageManager *>(pKernel->GetService("package"));
+ PackageManager *pPM = pKernel->getPackage();
BS_ASSERT(pPM);
return pPM;
}
@@ -92,17 +90,15 @@ static int getAbsolutePath(lua_State *L) {
}
static int getFileSize(lua_State *L) {
- PackageManager *pPM = getPM();
-
- lua_pushnumber(L, pPM->getFileSize(luaL_checkstring(L, 1)));
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
return 1;
}
static int getFileType(lua_State *L) {
- PackageManager *pPM = getPM();
-
- lua_pushnumber(L, pPM->getFileType(luaL_checkstring(L, 1)));
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
return 1;
}
@@ -199,9 +195,9 @@ static const luaL_reg PACKAGE_FUNCTIONS[] = {
};
bool PackageManager::registerScriptBindings() {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pScript = pKernel->getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
diff --git a/engines/sword25/script/lua_extensions.cpp b/engines/sword25/script/lua_extensions.cpp
index 3c0d4570a2..25a43e17d2 100644
--- a/engines/sword25/script/lua_extensions.cpp
+++ b/engines/sword25/script/lua_extensions.cpp
@@ -47,7 +47,7 @@ static int warning(lua_State *L) {
lua_pushstring(L, "WARNING - ");
lua_pushvalue(L, 1);
lua_concat(L, 3);
- BS_Log::Log("%s\n", luaL_checkstring(L, -1));
+ BS_Log::log("%s\n", luaL_checkstring(L, -1));
lua_pop(L, 1);
#ifdef DEBUG
diff --git a/engines/sword25/script/luabindhelper.h b/engines/sword25/script/luabindhelper.h
index 0dbaaa3186..dc45104d53 100644
--- a/engines/sword25/script/luabindhelper.h
+++ b/engines/sword25/script/luabindhelper.h
@@ -37,17 +37,8 @@
#include "sword25/kernel/common.h"
-namespace Lua {
-
-extern "C"
-{
#include "sword25/util/lua/lua.h"
#include "sword25/util/lua/lauxlib.h"
-}
-
-}
-
-using namespace Lua;
namespace Sword25 {
diff --git a/engines/sword25/script/luacallback.cpp b/engines/sword25/script/luacallback.cpp
index 6d2e634632..bb2c821aa8 100644
--- a/engines/sword25/script/luacallback.cpp
+++ b/engines/sword25/script/luacallback.cpp
@@ -35,18 +35,11 @@
#include "sword25/script/luacallback.h"
#include "sword25/script/luabindhelper.h"
-namespace Lua {
-
-extern "C"
-{
#include "sword25/util/lua/lua.h"
#include "sword25/util/lua/lauxlib.h"
-}
const char *CALLBACKTABLE_NAME = "__CALLBACKS";
-}
-
namespace Sword25 {
#define BS_LOG_PREFIX "LUA"
diff --git a/engines/sword25/script/luacallback.h b/engines/sword25/script/luacallback.h
index 0a5dec17d9..e097f5b499 100644
--- a/engines/sword25/script/luacallback.h
+++ b/engines/sword25/script/luacallback.h
@@ -37,14 +37,8 @@
#include "sword25/kernel/common.h"
-namespace Lua {
-
struct lua_State;
-}
-
-using namespace Lua;
-
namespace Sword25 {
class LuaCallback {
diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp
index 82166f7c25..aa2bdb9e06 100644
--- a/engines/sword25/script/luascript.cpp
+++ b/engines/sword25/script/luascript.cpp
@@ -45,21 +45,13 @@
#include "sword25/kernel/outputpersistenceblock.h"
#include "sword25/kernel/inputpersistenceblock.h"
-namespace Lua {
-
-extern "C" {
#include "sword25/util/lua/lua.h"
#include "sword25/util/lua/lualib.h"
#include "sword25/util/lua/lauxlib.h"
#include "sword25/util/pluto/pluto.h"
-}
-
-}
namespace Sword25 {
-using namespace Lua;
-
LuaScriptEngine::LuaScriptEngine(Kernel *KernelPtr) :
ScriptEngine(KernelPtr),
_state(0),
@@ -72,10 +64,6 @@ LuaScriptEngine::~LuaScriptEngine() {
lua_close(_state);
}
-Service *LuaScriptEngine_CreateObject(Kernel *KernelPtr) {
- return new LuaScriptEngine(KernelPtr);
-}
-
namespace {
int panicCB(lua_State *L) {
BS_LOG_ERRORLN("Lua panic. Error message: %s", lua_isnil(L, -1) ? "" : lua_tostring(L, -1));
@@ -159,7 +147,7 @@ bool LuaScriptEngine::executeFile(const Common::String &fileName) {
debug(2, "LuaScriptEngine::executeFile(%s)", fileName.c_str());
// Get a pointer to the package manager
- PackageManager *pPackage = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
BS_ASSERT(pPackage);
// File read
@@ -431,7 +419,7 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) {
pluto_persist(_state, chunkwriter, &chunkData);
// Persistenzdaten in den Writer schreiben.
- writer.write(&chunkData[0], chunkData.size());
+ writer.writeByteArray(chunkData);
// Die beiden Tabellen vom Stack nehmen.
lua_pop(_state, 2);
@@ -528,7 +516,7 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) {
// Persisted Lua data
Common::Array<byte> chunkData;
- reader.read(chunkData);
+ reader.readByteArray(chunkData);
// Chunk-Reader initialisation. It is used with pluto_unpersist to restore read data
ChunkreaderData cd;
diff --git a/engines/sword25/script/luascript.h b/engines/sword25/script/luascript.h
index f557ae45f1..b66c32176a 100644
--- a/engines/sword25/script/luascript.h
+++ b/engines/sword25/script/luascript.h
@@ -39,14 +39,7 @@
#include "common/str-array.h"
#include "sword25/kernel/common.h"
#include "sword25/script/script.h"
-
-namespace Lua {
-
-struct lua_State;
-
-}
-
-using namespace Lua;
+#include "sword25/util/lua/lua.h"
namespace Sword25 {
diff --git a/engines/sword25/script/script.h b/engines/sword25/script/script.h
index 2f72cf0cc8..9ca146026e 100644
--- a/engines/sword25/script/script.h
+++ b/engines/sword25/script/script.h
@@ -49,8 +49,8 @@ class BS_InputPersistenceBlock;
class ScriptEngine : public Service, public Persistable {
public:
- ScriptEngine(Kernel *KernelPtr) : Service(KernelPtr) {};
- virtual ~ScriptEngine() {};
+ ScriptEngine(Kernel *KernelPtr) : Service(KernelPtr) {}
+ virtual ~ScriptEngine() {}
// -----------------------------------------------------------------------------
// This method must be implemented by the script engine
diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp
index c753afd9b9..fa39639f51 100644
--- a/engines/sword25/sfx/soundengine.cpp
+++ b/engines/sword25/sfx/soundengine.cpp
@@ -67,10 +67,6 @@ SoundEngine::SoundEngine(Kernel *pKernel) : ResourceService(pKernel) {
_handles[i].type = kFreeHandle;
}
-Service *SoundEngine_CreateObject(Kernel *pKernel) {
- return new SoundEngine(pKernel);
-}
-
bool SoundEngine::init(uint sampleRate, uint channels) {
warning("STUB: SoundEngine::init(%d, %d)", sampleRate, channels);
@@ -157,7 +153,7 @@ bool SoundEngine::playSound(const Common::String &fileName, SOUND_TYPES type, fl
}
uint SoundEngine::playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume, float pan, bool loop, int loopStart, int loopEnd, uint layer) {
- Common::SeekableReadStream *in = Kernel::GetInstance()->GetPackage()->getStream(fileName);
+ Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(fileName);
Audio::SeekableAudioStream *stream = Audio::makeVorbisStream(in, DisposeAfterUse::YES);
uint id;
SndHandle *handle = getHandle(&id);
diff --git a/engines/sword25/sfx/soundengine.h b/engines/sword25/sfx/soundengine.h
index 81383b12cb..b8c10cc293 100644
--- a/engines/sword25/sfx/soundengine.h
+++ b/engines/sword25/sfx/soundengine.h
@@ -88,7 +88,7 @@ public:
typedef void (*DynamicSoundReadCallback)(void *UserData, void *Data, uint DataLength);
SoundEngine(Kernel *pKernel);
- ~SoundEngine() {};
+ ~SoundEngine() {}
/**
* Initialises the sound engine
diff --git a/engines/sword25/sfx/soundengine_script.cpp b/engines/sword25/sfx/soundengine_script.cpp
index 2808296799..eabbef6e5e 100644
--- a/engines/sword25/sfx/soundengine_script.cpp
+++ b/engines/sword25/sfx/soundengine_script.cpp
@@ -46,9 +46,9 @@
namespace Sword25 {
static int init(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
if (lua_gettop(L) == 0)
@@ -62,9 +62,9 @@ static int init(lua_State *L) {
}
static int update(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->update();
@@ -73,9 +73,9 @@ static int update(lua_State *L) {
}
static int setVolume(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->setVolume(static_cast<float>(luaL_checknumber(L, 1)),
@@ -85,9 +85,9 @@ static int setVolume(lua_State *L) {
}
static int getVolume(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
lua_pushnumber(L, pSfx->getVolume(static_cast<SoundEngine::SOUND_TYPES>(static_cast<uint>(luaL_checknumber(L, 1)))));
@@ -96,9 +96,9 @@ static int getVolume(lua_State *L) {
}
static int pauseAll(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->pauseAll();
@@ -107,9 +107,9 @@ static int pauseAll(lua_State *L) {
}
static int resumeAll(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->resumeAll();
@@ -118,9 +118,9 @@ static int resumeAll(lua_State *L) {
}
static int pauseLayer(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->pauseLayer(static_cast<int>(luaL_checknumber(L, 1)));
@@ -129,9 +129,9 @@ static int pauseLayer(lua_State *L) {
}
static int resumeLayer(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->resumeLayer(static_cast<int>(luaL_checknumber(L, 1)));
@@ -176,9 +176,9 @@ static void processPlayParams(lua_State *L, Common::String &fileName, SoundEngin
}
static int playSound(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
Common::String fileName;
@@ -197,9 +197,9 @@ static int playSound(lua_State *L) {
}
static int playSoundEx(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
Common::String fileName;
@@ -218,9 +218,9 @@ static int playSoundEx(lua_State *L) {
}
static int setSoundVolume(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->setSoundVolume(static_cast<uint>(luaL_checknumber(L, 1)), static_cast<float>(luaL_checknumber(L, 2)));
@@ -229,9 +229,9 @@ static int setSoundVolume(lua_State *L) {
}
static int setSoundPanning(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->setSoundPanning(static_cast<uint>(luaL_checknumber(L, 1)), static_cast<float>(luaL_checknumber(L, 2)));
@@ -240,9 +240,9 @@ static int setSoundPanning(lua_State *L) {
}
static int pauseSound(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->pauseSound(static_cast<uint>(luaL_checknumber(L, 1)));
@@ -251,9 +251,9 @@ static int pauseSound(lua_State *L) {
}
static int resumeSound(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->resumeSound(static_cast<uint>(luaL_checknumber(L, 1)));
@@ -262,9 +262,9 @@ static int resumeSound(lua_State *L) {
}
static int stopSound(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
pSfx->stopSound(static_cast<uint>(luaL_checknumber(L, 1)));
@@ -273,9 +273,9 @@ static int stopSound(lua_State *L) {
}
static int isSoundPaused(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
lua_pushbooleancpp(L, pSfx->isSoundPaused(static_cast<uint>(luaL_checknumber(L, 1))));
@@ -284,9 +284,9 @@ static int isSoundPaused(lua_State *L) {
}
static int isSoundPlaying(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
lua_pushbooleancpp(L, pSfx->isSoundPlaying(static_cast<uint>(luaL_checknumber(L, 1))));
@@ -295,9 +295,9 @@ static int isSoundPlaying(lua_State *L) {
}
static int getSoundVolume(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
lua_pushnumber(L, pSfx->getSoundVolume(static_cast<uint>(luaL_checknumber(L, 1))));
@@ -306,9 +306,9 @@ static int getSoundVolume(lua_State *L) {
}
static int getSoundPanning(lua_State *L) {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ SoundEngine *pSfx = pKernel->getSfx();
BS_ASSERT(pSfx);
lua_pushnumber(L, pSfx->getSoundPanning(static_cast<uint>(luaL_checknumber(L, 1))));
@@ -349,9 +349,9 @@ static const lua_constant_reg SFX_CONSTANTS[] = {
};
bool SoundEngine::registerScriptBindings() {
- Kernel *pKernel = Kernel::GetInstance();
+ Kernel *pKernel = Kernel::getInstance();
BS_ASSERT(pKernel);
- ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ ScriptEngine *pScript = pKernel->getScript();
BS_ASSERT(pScript);
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
BS_ASSERT(L);
diff --git a/engines/sword25/sword25.cpp b/engines/sword25/sword25.cpp
index 4e99ade25d..4c188fe7ea 100644
--- a/engines/sword25/sword25.cpp
+++ b/engines/sword25/sword25.cpp
@@ -39,9 +39,14 @@
#include "sword25/sword25.h"
#include "sword25/kernel/filesystemutil.h"
#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/persistenceservice.h"
#include "sword25/package/packagemanager.h"
#include "sword25/script/script.h"
+#include "sword25/gfx/animationtemplateregistry.h" // Needed so we can destroy the singleton
+#include "sword25/gfx/renderobjectregistry.h" // Needed so we can destroy the singleton
+#include "sword25/math/regionregistry.h" // Needed so we can destroy the singleton
+
namespace Sword25 {
#define BS_LOG_PREFIX "MAIN"
@@ -49,10 +54,6 @@ namespace Sword25 {
const char *const PACKAGE_MANAGER = "archiveFS";
const char *const DEFAULT_SCRIPT_FILE = "/system/boot.lua";
-void logToStdout(const char *Message) {
- debugN(0, "%s", Message);
-}
-
Sword25Engine::Sword25Engine(OSystem *syst, const ADGameDescription *gameDesc):
Engine(syst),
_gameDescription(gameDesc) {
@@ -60,9 +61,13 @@ Sword25Engine::Sword25Engine(OSystem *syst, const ADGameDescription *gameDesc):
DebugMan.addDebugChannel(kDebugScript, "Script", "Script debug level");
DebugMan.addDebugChannel(kDebugScript, "Scripts", "Script debug level");
DebugMan.addDebugChannel(kDebugSound, "Sound", "Sound debug level");
+
+ _console = new Sword25Console(this);
}
Sword25Engine::~Sword25Engine() {
+ DebugMan.clearAllDebugChannels();
+ delete _console;
}
Common::Error Sword25Engine::run() {
@@ -83,9 +88,6 @@ Common::Error Sword25Engine::run() {
}
Common::Error Sword25Engine::appStart() {
- // All log messages will be sent to StdOut
- BS_Log::RegisterLogListener(logToStdout);
-
// Initialise the graphics mode to ARGB8888
Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
initGraphics(800, 600, true, &format);
@@ -93,19 +95,13 @@ Common::Error Sword25Engine::appStart() {
return Common::kUnsupportedColorMode;
// Kernel initialization
- if (!Kernel::GetInstance()->GetInitSuccess()) {
+ if (!Kernel::getInstance()->getInitSuccess()) {
BS_LOG_ERRORLN("Kernel initialization failed.");
return Common::kUnknownError;
}
- // Package-Manager starten, damit die Packfiles geladen werden können.
- PackageManager *packageManagerPtr = static_cast<PackageManager *>(Kernel::GetInstance()->NewService("package", PACKAGE_MANAGER));
- if (!packageManagerPtr) {
- BS_LOG_ERRORLN("PackageManager initialization failed.");
- return Common::kUnknownError;
- }
-
- // Packages laden oder das aktuelle Verzeichnis mounten, wenn das über Kommandozeile angefordert wurde.
+ // Load packages
+ PackageManager *packageManagerPtr = Kernel::getInstance()->getPackage();
if (getGameFlags() & GF_EXTRACTED) {
if (!packageManagerPtr->loadDirectoryAsPackage(ConfMan.get("path"), "/"))
return Common::kUnknownError;
@@ -114,13 +110,16 @@ Common::Error Sword25Engine::appStart() {
return Common::kUnknownError;
}
- // Einen Pointer auf den Skript-Engine holen.
- ScriptEngine *scriptPtr = static_cast<ScriptEngine *>(Kernel::GetInstance()->GetService("script"));
+ // Pass the command line to the script engine.
+ ScriptEngine *scriptPtr = Kernel::getInstance()->getScript();
if (!scriptPtr) {
BS_LOG_ERRORLN("Script intialization failed.");
return Common::kUnknownError;
}
+ // Set the game target for use in savegames
+ setGameTarget(_targetName.c_str());
+
Common::StringArray commandParameters;
scriptPtr->setCommandLine(commandParameters);
@@ -129,7 +128,7 @@ Common::Error Sword25Engine::appStart() {
bool Sword25Engine::appMain() {
// The main script start. This script loads all the other scripts and starts the actual game.
- ScriptEngine *scriptPtr = static_cast<ScriptEngine *>(Kernel::GetInstance()->GetService("script"));
+ ScriptEngine *scriptPtr = Kernel::getInstance()->getScript();
BS_ASSERT(scriptPtr);
scriptPtr->executeFile(DEFAULT_SCRIPT_FILE);
@@ -138,20 +137,25 @@ bool Sword25Engine::appMain() {
bool Sword25Engine::appEnd() {
// The kernel is shutdown, and un-initialises all subsystems
- Kernel::DeleteInstance();
+ Kernel::deleteInstance();
+
+ AnimationTemplateRegistry::destroy();
+ RenderObjectRegistry::destroy();
+ RegionRegistry::destroy();
// Free the log file if it was used
- BS_Log::_CloseLog();
+ BS_Log::closeLog();
return true;
}
bool Sword25Engine::loadPackages() {
- PackageManager *packageManagerPtr = reinterpret_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ PackageManager *packageManagerPtr = Kernel::getInstance()->getPackage();
BS_ASSERT(packageManagerPtr);
// Load the main package
- if (!packageManagerPtr->loadPackage("data.b25c", "/")) return false;
+ if (!packageManagerPtr->loadPackage("data.b25c", "/"))
+ return false;
// Get the contents of the main program directory and sort them alphabetically
Common::FSNode dir(ConfMan.get("path"));
@@ -163,7 +167,7 @@ bool Sword25Engine::loadPackages() {
Common::sort(files.begin(), files.end());
- // Identity all patch packages
+ // Identify all patch packages
// The filename of patch packages must have the form patch??.b25c, with the question marks
// are placeholders for numbers.
// Since the filenames have been sorted, patches are mounted with low numbers first, through
@@ -175,7 +179,7 @@ bool Sword25Engine::loadPackages() {
return false;
}
- // Identity and mount all language packages
+ // Identify and mount all language packages
// The filename of the packages have the form lang_*.b25c (eg. lang_de.b25c)
for (Common::FSList::const_iterator it = files.begin(); it != files.end(); ++it) {
if (it->getName().matchString("lang_*.b25c", true))
@@ -186,4 +190,17 @@ bool Sword25Engine::loadPackages() {
return true;
}
+bool Sword25Engine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL);
+ // TODO: Implement more of these features?!
+#if 0
+ return
+ (f == kSupportsSubtitleOptions) ||
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+#endif
+}
+
} // End of namespace Sword25
diff --git a/engines/sword25/sword25.h b/engines/sword25/sword25.h
index 8f31a05562..91b62cba09 100644
--- a/engines/sword25/sword25.h
+++ b/engines/sword25/sword25.h
@@ -32,9 +32,18 @@
#include "engines/engine.h"
#include "sword25/kernel/log.h"
+#include "sword25/console.h"
struct ADGameDescription;
+/**
+ * This is the namespace of the Sword25 engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Broken Sword 2.5
+ */
namespace Sword25 {
enum {
@@ -62,8 +71,20 @@ private:
bool loadPackages();
+ Sword25Console *_console;
+
protected:
virtual Common::Error run();
+ bool hasFeature(EngineFeature f) const;
+// void pauseEngineIntern(bool pause); // TODO: Implement this!!!
+// void syncSoundSettings(); // TODO: Implement this!!!
+// Common::Error loadGameState(int slot); // TODO: Implement this?
+// Common::Error saveGameState(int slot, const char *desc); // TODO: Implement this?
+// bool canLoadGameStateCurrently(); // TODO: Implement this?
+// bool canSaveGameStateCurrently(); // TODO: Implement this?
+
+ GUI::Debugger *getDebugger() { return _console; }
+
void shutdown();
public:
diff --git a/engines/sword25/util/lua/lapi.c b/engines/sword25/util/lua/lapi.cpp
index d7e8931e45..b1118db368 100644
--- a/engines/sword25/util/lua/lapi.c
+++ b/engines/sword25/util/lua/lapi.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lapi.c,v 2.55.1.3 2008/01/03 15:20:39 roberto Exp $
+** $Id$
** Lua API
** See Copyright Notice in lua.h
*/
@@ -32,9 +32,9 @@
const char lua_ident[] =
- "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n"
- "$Authors: " LUA_AUTHORS " $\n"
- "$URL: www.lua.org $\n";
+ "Lua: " LUA_RELEASE " " LUA_COPYRIGHT " \n"
+ "Authors: " LUA_AUTHORS " \n"
+ "URL: www.lua.org\n";
@@ -50,7 +50,8 @@ static TValue *index2adr (lua_State *L, int idx) {
if (idx > 0) {
TValue *o = L->base + (idx - 1);
api_check(L, idx <= L->ci->top - L->base);
- if (o >= L->top) return cast(TValue *, luaO_nilobject);
+ // FIXME: Get rid of const_cast
+ if (o >= L->top) return const_cast<TValue *>(luaO_nilobject);
else return o;
}
else if (idx > LUA_REGISTRYINDEX) {
@@ -70,7 +71,8 @@ static TValue *index2adr (lua_State *L, int idx) {
idx = LUA_GLOBALSINDEX - idx;
return (idx <= func->c.nupvalues)
? &func->c.upvalue[idx-1]
- : cast(TValue *, luaO_nilobject);
+ // FIXME: Get rid of const_cast
+ : const_cast<TValue *>(luaO_nilobject);
}
}
}
diff --git a/engines/sword25/util/lua/lapi.h b/engines/sword25/util/lua/lapi.h
index 2c3fab244e..f968ffc992 100644
--- a/engines/sword25/util/lua/lapi.h
+++ b/engines/sword25/util/lua/lapi.h
@@ -1,5 +1,5 @@
/*
-** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Auxiliary functions from Lua API
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lauxlib.c b/engines/sword25/util/lua/lauxlib.cpp
index 10f14e2c08..53c0556625 100644
--- a/engines/sword25/util/lua/lauxlib.c
+++ b/engines/sword25/util/lua/lauxlib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $
+** $Id$
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lauxlib.h b/engines/sword25/util/lua/lauxlib.h
index 34258235db..d58f290527 100644
--- a/engines/sword25/util/lua/lauxlib.h
+++ b/engines/sword25/util/lua/lauxlib.h
@@ -1,5 +1,5 @@
/*
-** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lbaselib.c b/engines/sword25/util/lua/lbaselib.cpp
index 8f97a1c246..5032e6322a 100644
--- a/engines/sword25/util/lua/lbaselib.c
+++ b/engines/sword25/util/lua/lbaselib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lbaselib.c,v 1.191.1.4 2008/01/20 13:53:22 roberto Exp $
+** $Id$
** Basic library
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lcode.c b/engines/sword25/util/lua/lcode.cpp
index cff626b7fa..6e7e10017f 100644
--- a/engines/sword25/util/lua/lcode.c
+++ b/engines/sword25/util/lua/lcode.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Code generator for Lua
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lcode.h b/engines/sword25/util/lua/lcode.h
index b941c60721..751b2b5695 100644
--- a/engines/sword25/util/lua/lcode.h
+++ b/engines/sword25/util/lua/lcode.h
@@ -1,5 +1,5 @@
/*
-** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Code generator for Lua
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ldblib.c b/engines/sword25/util/lua/ldblib.cpp
index 67de1222a9..b2e249e9b7 100644
--- a/engines/sword25/util/lua/ldblib.c
+++ b/engines/sword25/util/lua/ldblib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
+** $Id$
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ldebug.c b/engines/sword25/util/lua/ldebug.cpp
index 9eac4a9b41..0b26522b31 100644
--- a/engines/sword25/util/lua/ldebug.c
+++ b/engines/sword25/util/lua/ldebug.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ldebug.c,v 2.29.1.3 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Debug Interface
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ldebug.h b/engines/sword25/util/lua/ldebug.h
index ba28a97248..22226b4096 100644
--- a/engines/sword25/util/lua/ldebug.h
+++ b/engines/sword25/util/lua/ldebug.h
@@ -1,5 +1,5 @@
/*
-** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Auxiliary functions from Debug Interface module
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ldo.c b/engines/sword25/util/lua/ldo.cpp
index 8de05f728e..07508fbb14 100644
--- a/engines/sword25/util/lua/ldo.c
+++ b/engines/sword25/util/lua/ldo.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $
+** $Id$
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ldo.h b/engines/sword25/util/lua/ldo.h
index 98fddac59f..4c97134805 100644
--- a/engines/sword25/util/lua/ldo.h
+++ b/engines/sword25/util/lua/ldo.h
@@ -1,5 +1,5 @@
/*
-** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ldump.c b/engines/sword25/util/lua/ldump.cpp
index c9d3d4870f..3ce16542d6 100644
--- a/engines/sword25/util/lua/ldump.c
+++ b/engines/sword25/util/lua/ldump.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** save precompiled Lua chunks
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lfunc.c b/engines/sword25/util/lua/lfunc.cpp
index 813e88f583..ce7acf4e77 100644
--- a/engines/sword25/util/lua/lfunc.c
+++ b/engines/sword25/util/lua/lfunc.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $
+** $Id$
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lfunc.h b/engines/sword25/util/lua/lfunc.h
index a68cf5151c..4c2b7fd138 100644
--- a/engines/sword25/util/lua/lfunc.h
+++ b/engines/sword25/util/lua/lfunc.h
@@ -1,5 +1,5 @@
/*
-** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lgc.c b/engines/sword25/util/lua/lgc.cpp
index d9e0b78294..52ff72bdc9 100644
--- a/engines/sword25/util/lua/lgc.c
+++ b/engines/sword25/util/lua/lgc.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Garbage Collector
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lgc.h b/engines/sword25/util/lua/lgc.h
index 5a8dc605b3..5123ccb479 100644
--- a/engines/sword25/util/lua/lgc.h
+++ b/engines/sword25/util/lua/lgc.h
@@ -1,5 +1,5 @@
/*
-** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Garbage Collector
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/linit.c b/engines/sword25/util/lua/linit.cpp
index c1f90dfab7..93f41d0350 100644
--- a/engines/sword25/util/lua/linit.c
+++ b/engines/sword25/util/lua/linit.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Initialization of libraries for lua.c
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/liolib.c b/engines/sword25/util/lua/liolib.cpp
index e79ed1cb2e..aa44dcafa3 100644
--- a/engines/sword25/util/lua/liolib.c
+++ b/engines/sword25/util/lua/liolib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
+** $Id$
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/llex.c b/engines/sword25/util/lua/llex.cpp
index 6dc319358c..fdde2b8e5f 100644
--- a/engines/sword25/util/lua/llex.c
+++ b/engines/sword25/util/lua/llex.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/llex.h b/engines/sword25/util/lua/llex.h
index a9201cee48..fa8b7a2a28 100644
--- a/engines/sword25/util/lua/llex.h
+++ b/engines/sword25/util/lua/llex.h
@@ -1,5 +1,5 @@
/*
-** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/llimits.h b/engines/sword25/util/lua/llimits.h
index ca8dcb7224..a31ad160ad 100644
--- a/engines/sword25/util/lua/llimits.h
+++ b/engines/sword25/util/lua/llimits.h
@@ -1,5 +1,5 @@
/*
-** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lmathlib.c b/engines/sword25/util/lua/lmathlib.cpp
index 441fbf736c..ed50539f1f 100644
--- a/engines/sword25/util/lua/lmathlib.c
+++ b/engines/sword25/util/lua/lmathlib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Standard mathematical library
** See Copyright Notice in lua.h
*/
@@ -252,7 +252,17 @@ LUALIB_API int luaopen_math (lua_State *L) {
luaL_register(L, LUA_MATHLIBNAME, mathlib);
lua_pushnumber(L, PI);
lua_setfield(L, -2, "pi");
+#if defined(MACOSX) && defined(__GNUC__) && ! defined( __XLC__ )
+ // WORKAROUND for a bug in the Mac OS X 10.2.8 SDK. It defines
+ // HUGE_VAL simply to 1e500, leading to this compiler error:
+ // error: floating constant exceeds range of 'double'
+ // However, GCC (at least the version we are using for our cross
+ // compiler) has a __builtin_huge_val which returns the correct
+ // value, so we just use that.
+ lua_pushnumber(L, __builtin_huge_val());
+#else
lua_pushnumber(L, HUGE_VAL);
+#endif
lua_setfield(L, -2, "huge");
#if defined(LUA_COMPAT_MOD)
lua_getfield(L, -1, "fmod");
diff --git a/engines/sword25/util/lua/lmem.c b/engines/sword25/util/lua/lmem.cpp
index ae7d8c965f..ccd69357e0 100644
--- a/engines/sword25/util/lua/lmem.c
+++ b/engines/sword25/util/lua/lmem.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lmem.h b/engines/sword25/util/lua/lmem.h
index 7c2dcb3220..97a888c7f8 100644
--- a/engines/sword25/util/lua/lmem.h
+++ b/engines/sword25/util/lua/lmem.h
@@ -1,5 +1,5 @@
/*
-** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/loadlib.c b/engines/sword25/util/lua/loadlib.cpp
index d955f3ef41..e060611450 100644
--- a/engines/sword25/util/lua/loadlib.c
+++ b/engines/sword25/util/lua/loadlib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: loadlib.c,v 1.52.1.2 2007/12/28 14:58:43 roberto Exp $
+** $Id$
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
diff --git a/engines/sword25/util/lua/lobject.c b/engines/sword25/util/lua/lobject.cpp
index 4ff50732a4..24718931ed 100644
--- a/engines/sword25/util/lua/lobject.c
+++ b/engines/sword25/util/lua/lobject.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Some generic functions over Lua objects
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lobject.h b/engines/sword25/util/lua/lobject.h
index e7199dfc68..35aaed028a 100644
--- a/engines/sword25/util/lua/lobject.h
+++ b/engines/sword25/util/lua/lobject.h
@@ -1,5 +1,5 @@
/*
-** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lopcodes.c b/engines/sword25/util/lua/lopcodes.cpp
index 4cc745230b..d9da16f689 100644
--- a/engines/sword25/util/lua/lopcodes.c
+++ b/engines/sword25/util/lua/lopcodes.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lopcodes.h b/engines/sword25/util/lua/lopcodes.h
index 41224d6ee1..e1aed0f637 100644
--- a/engines/sword25/util/lua/lopcodes.h
+++ b/engines/sword25/util/lua/lopcodes.h
@@ -1,5 +1,5 @@
/*
-** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/loslib.c b/engines/sword25/util/lua/loslib.cpp
index da06a572ac..70a67bccf7 100644
--- a/engines/sword25/util/lua/loslib.c
+++ b/engines/sword25/util/lua/loslib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
+** $Id$
** Standard Operating System library
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lparser.c b/engines/sword25/util/lua/lparser.cpp
index 1e2a9a88b7..03ea333315 100644
--- a/engines/sword25/util/lua/lparser.c
+++ b/engines/sword25/util/lua/lparser.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Lua Parser
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lparser.h b/engines/sword25/util/lua/lparser.h
index 18836afd1c..f9b8e24913 100644
--- a/engines/sword25/util/lua/lparser.h
+++ b/engines/sword25/util/lua/lparser.h
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Lua Parser
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lstate.c b/engines/sword25/util/lua/lstate.cpp
index 4313b83a0c..495d75c8a6 100644
--- a/engines/sword25/util/lua/lstate.c
+++ b/engines/sword25/util/lua/lstate.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $
+** $Id$
** Global State
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lstate.h b/engines/sword25/util/lua/lstate.h
index 3bc575b6bc..94a6249461 100644
--- a/engines/sword25/util/lua/lstate.h
+++ b/engines/sword25/util/lua/lstate.h
@@ -1,5 +1,5 @@
/*
-** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** $Id$
** Global State
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lstring.c b/engines/sword25/util/lua/lstring.cpp
index 49113151cc..cd55cc63bf 100644
--- a/engines/sword25/util/lua/lstring.c
+++ b/engines/sword25/util/lua/lstring.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** String table (keeps all strings handled by Lua)
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lstring.h b/engines/sword25/util/lua/lstring.h
index 73a2ff8b38..c88e4c12a9 100644
--- a/engines/sword25/util/lua/lstring.h
+++ b/engines/sword25/util/lua/lstring.h
@@ -1,5 +1,5 @@
/*
-** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** String table (keep all strings handled by Lua)
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lstrlib.c b/engines/sword25/util/lua/lstrlib.cpp
index ca333ba168..e5501b9b49 100644
--- a/engines/sword25/util/lua/lstrlib.c
+++ b/engines/sword25/util/lua/lstrlib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lstrlib.c,v 1.132.1.3 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ltable.c b/engines/sword25/util/lua/ltable.cpp
index ec84f4fabc..b2ec0e912a 100644
--- a/engines/sword25/util/lua/ltable.c
+++ b/engines/sword25/util/lua/ltable.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
@@ -272,7 +272,8 @@ static void setarrayvector (lua_State *L, Table *t, int size) {
static void setnodevector (lua_State *L, Table *t, int size) {
int lsize;
if (size == 0) { /* no elements to hash part? */
- t->node = cast(Node *, dummynode); /* use common `dummynode' */
+ // FIXME: Get rid of const_cast
+ t->node = const_cast<Node *>(dummynode); /* use common `dummynode' */
lsize = 0;
}
else {
@@ -364,7 +365,8 @@ Table *luaH_new (lua_State *L, int narray, int nhash) {
t->array = NULL;
t->sizearray = 0;
t->lsizenode = 0;
- t->node = cast(Node *, dummynode);
+ // FIXME: Get rid of const_cast
+ t->node = const_cast<Node *>(dummynode);
setarrayvector(L, t, narray);
setnodevector(L, t, nhash);
return t;
@@ -495,7 +497,8 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key);
t->flags = 0;
if (p != luaO_nilobject)
- return cast(TValue *, p);
+ // FIXME: Get rid of const_cast
+ return const_cast<TValue *>(p);
else {
if (ttisnil(key)) luaG_runerror(L, "table index is nil");
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
@@ -508,7 +511,8 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
TValue *luaH_setnum (lua_State *L, Table *t, int key) {
const TValue *p = luaH_getnum(t, key);
if (p != luaO_nilobject)
- return cast(TValue *, p);
+ // FIXME: Get rid of const_cast
+ return const_cast<TValue *>(p);
else {
TValue k;
setnvalue(&k, cast_num(key));
@@ -520,7 +524,8 @@ TValue *luaH_setnum (lua_State *L, Table *t, int key) {
TValue *luaH_setstr (lua_State *L, Table *t, TString *key) {
const TValue *p = luaH_getstr(t, key);
if (p != luaO_nilobject)
- return cast(TValue *, p);
+ // FIXME: Get rid of const_cast
+ return const_cast<TValue *>(p);
else {
TValue k;
setsvalue(L, &k, key);
diff --git a/engines/sword25/util/lua/ltable.h b/engines/sword25/util/lua/ltable.h
index f5b9d5ead0..aa28914871 100644
--- a/engines/sword25/util/lua/ltable.h
+++ b/engines/sword25/util/lua/ltable.h
@@ -1,5 +1,5 @@
/*
-** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ltablib.c b/engines/sword25/util/lua/ltablib.cpp
index 06f1c37be1..607c09ae71 100644
--- a/engines/sword25/util/lua/ltablib.c
+++ b/engines/sword25/util/lua/ltablib.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ltablib.c,v 1.38.1.2 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Library for Table Manipulation
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ltm.c b/engines/sword25/util/lua/ltm.cpp
index c27f0f6fab..02856a58fc 100644
--- a/engines/sword25/util/lua/ltm.c
+++ b/engines/sword25/util/lua/ltm.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Tag methods
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/ltm.h b/engines/sword25/util/lua/ltm.h
index 64343b781b..1b89683ef3 100644
--- a/engines/sword25/util/lua/ltm.h
+++ b/engines/sword25/util/lua/ltm.h
@@ -1,5 +1,5 @@
/*
-** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Tag methods
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lua.c b/engines/sword25/util/lua/lua.c
deleted file mode 100644
index 3a46609328..0000000000
--- a/engines/sword25/util/lua/lua.c
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
-** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
-** Lua stand-alone interpreter
-** See Copyright Notice in lua.h
-*/
-
-
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define lua_c
-
-#include "lua.h"
-
-#include "lauxlib.h"
-#include "lualib.h"
-
-
-
-static lua_State *globalL = NULL;
-
-static const char *progname = LUA_PROGNAME;
-
-
-
-static void lstop (lua_State *L, lua_Debug *ar) {
- (void)ar; /* unused arg. */
- lua_sethook(L, NULL, 0, 0);
- luaL_error(L, "interrupted!");
-}
-
-
-static void laction (int i) {
- signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
- terminate process (default action) */
- lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
-}
-
-
-static void print_usage (void) {
- fprintf(stderr,
- "usage: %s [options] [script [args]].\n"
- "Available options are:\n"
- " -e stat execute string " LUA_QL("stat") "\n"
- " -l name require library " LUA_QL("name") "\n"
- " -i enter interactive mode after executing " LUA_QL("script") "\n"
- " -v show version information\n"
- " -- stop handling options\n"
- " - execute stdin and stop handling options\n"
- ,
- progname);
- fflush(stderr);
-}
-
-
-static void l_message (const char *pname, const char *msg) {
- if (pname) fprintf(stderr, "%s: ", pname);
- fprintf(stderr, "%s\n", msg);
- fflush(stderr);
-}
-
-
-static int report (lua_State *L, int status) {
- if (status && !lua_isnil(L, -1)) {
- const char *msg = lua_tostring(L, -1);
- if (msg == NULL) msg = "(error object is not a string)";
- l_message(progname, msg);
- lua_pop(L, 1);
- }
- return status;
-}
-
-
-static int traceback (lua_State *L) {
- if (!lua_isstring(L, 1)) /* 'message' not a string? */
- return 1; /* keep it intact */
- lua_getfield(L, LUA_GLOBALSINDEX, "debug");
- if (!lua_istable(L, -1)) {
- lua_pop(L, 1);
- return 1;
- }
- lua_getfield(L, -1, "traceback");
- if (!lua_isfunction(L, -1)) {
- lua_pop(L, 2);
- return 1;
- }
- lua_pushvalue(L, 1); /* pass error message */
- lua_pushinteger(L, 2); /* skip this function and traceback */
- lua_call(L, 2, 1); /* call debug.traceback */
- return 1;
-}
-
-
-static int docall (lua_State *L, int narg, int clear) {
- int status;
- int base = lua_gettop(L) - narg; /* function index */
- lua_pushcfunction(L, traceback); /* push traceback function */
- lua_insert(L, base); /* put it under chunk and args */
- signal(SIGINT, laction);
- status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
- signal(SIGINT, SIG_DFL);
- lua_remove(L, base); /* remove traceback function */
- /* force a complete garbage collection in case of errors */
- if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
- return status;
-}
-
-
-static void print_version (void) {
- l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT);
-}
-
-
-static int getargs (lua_State *L, char **argv, int n) {
- int narg;
- int i;
- int argc = 0;
- while (argv[argc]) argc++; /* count total number of arguments */
- narg = argc - (n + 1); /* number of arguments to the script */
- luaL_checkstack(L, narg + 3, "too many arguments to script");
- for (i=n+1; i < argc; i++)
- lua_pushstring(L, argv[i]);
- lua_createtable(L, narg, n + 1);
- for (i=0; i < argc; i++) {
- lua_pushstring(L, argv[i]);
- lua_rawseti(L, -2, i - n);
- }
- return narg;
-}
-
-
-static int dofile (lua_State *L, const char *name) {
- int status = luaL_loadfile(L, name) || docall(L, 0, 1);
- return report(L, status);
-}
-
-
-static int dostring (lua_State *L, const char *s, const char *name) {
- int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
- return report(L, status);
-}
-
-
-static int dolibrary (lua_State *L, const char *name) {
- lua_getglobal(L, "require");
- lua_pushstring(L, name);
- return report(L, docall(L, 1, 1));
-}
-
-
-static const char *get_prompt (lua_State *L, int firstline) {
- const char *p;
- lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
- p = lua_tostring(L, -1);
- if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
- lua_pop(L, 1); /* remove global */
- return p;
-}
-
-
-static int incomplete (lua_State *L, int status) {
- if (status == LUA_ERRSYNTAX) {
- size_t lmsg;
- const char *msg = lua_tolstring(L, -1, &lmsg);
- const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
- if (strstr(msg, LUA_QL("<eof>")) == tp) {
- lua_pop(L, 1);
- return 1;
- }
- }
- return 0; /* else... */
-}
-
-
-static int pushline (lua_State *L, int firstline) {
- char buffer[LUA_MAXINPUT];
- char *b = buffer;
- size_t l;
- const char *prmt = get_prompt(L, firstline);
- if (lua_readline(L, b, prmt) == 0)
- return 0; /* no input */
- l = strlen(b);
- if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
- b[l-1] = '\0'; /* remove it */
- if (firstline && b[0] == '=') /* first line starts with `=' ? */
- lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
- else
- lua_pushstring(L, b);
- lua_freeline(L, b);
- return 1;
-}
-
-
-static int loadline (lua_State *L) {
- int status;
- lua_settop(L, 0);
- if (!pushline(L, 1))
- return -1; /* no input */
- for (;;) { /* repeat until gets a complete line */
- status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
- if (!incomplete(L, status)) break; /* cannot try to add lines? */
- if (!pushline(L, 0)) /* no more input? */
- return -1;
- lua_pushliteral(L, "\n"); /* add a new line... */
- lua_insert(L, -2); /* ...between the two lines */
- lua_concat(L, 3); /* join them */
- }
- lua_saveline(L, 1);
- lua_remove(L, 1); /* remove line */
- return status;
-}
-
-
-static void dotty (lua_State *L) {
- int status;
- const char *oldprogname = progname;
- progname = NULL;
- while ((status = loadline(L)) != -1) {
- if (status == 0) status = docall(L, 0, 0);
- report(L, status);
- if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
- lua_getglobal(L, "print");
- lua_insert(L, 1);
- if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
- l_message(progname, lua_pushfstring(L,
- "error calling " LUA_QL("print") " (%s)",
- lua_tostring(L, -1)));
- }
- }
- lua_settop(L, 0); /* clear stack */
- fputs("\n", stdout);
- fflush(stdout);
- progname = oldprogname;
-}
-
-
-static int handle_script (lua_State *L, char **argv, int n) {
- int status;
- const char *fname;
- int narg = getargs(L, argv, n); /* collect arguments */
- lua_setglobal(L, "arg");
- fname = argv[n];
- if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
- fname = NULL; /* stdin */
- status = luaL_loadfile(L, fname);
- lua_insert(L, -(narg+1));
- if (status == 0)
- status = docall(L, narg, 0);
- else
- lua_pop(L, narg);
- return report(L, status);
-}
-
-
-/* check that argument has no extra characters at the end */
-#define notail(x) {if ((x)[2] != '\0') return -1;}
-
-
-static int collectargs (char **argv, int *pi, int *pv, int *pe) {
- int i;
- for (i = 1; argv[i] != NULL; i++) {
- if (argv[i][0] != '-') /* not an option? */
- return i;
- switch (argv[i][1]) { /* option */
- case '-':
- notail(argv[i]);
- return (argv[i+1] != NULL ? i+1 : 0);
- case '\0':
- return i;
- case 'i':
- notail(argv[i]);
- *pi = 1; /* go through */
- case 'v':
- notail(argv[i]);
- *pv = 1;
- break;
- case 'e':
- *pe = 1; /* go through */
- case 'l':
- if (argv[i][2] == '\0') {
- i++;
- if (argv[i] == NULL) return -1;
- }
- break;
- default: return -1; /* invalid option */
- }
- }
- return 0;
-}
-
-
-static int runargs (lua_State *L, char **argv, int n) {
- int i;
- for (i = 1; i < n; i++) {
- if (argv[i] == NULL) continue;
- lua_assert(argv[i][0] == '-');
- switch (argv[i][1]) { /* option */
- case 'e': {
- const char *chunk = argv[i] + 2;
- if (*chunk == '\0') chunk = argv[++i];
- lua_assert(chunk != NULL);
- if (dostring(L, chunk, "=(command line)") != 0)
- return 1;
- break;
- }
- case 'l': {
- const char *filename = argv[i] + 2;
- if (*filename == '\0') filename = argv[++i];
- lua_assert(filename != NULL);
- if (dolibrary(L, filename))
- return 1; /* stop if file fails */
- break;
- }
- default: break;
- }
- }
- return 0;
-}
-
-
-static int handle_luainit (lua_State *L) {
- const char *init = getenv(LUA_INIT);
- if (init == NULL) return 0; /* status OK */
- else if (init[0] == '@')
- return dofile(L, init+1);
- else
- return dostring(L, init, "=" LUA_INIT);
-}
-
-
-struct Smain {
- int argc;
- char **argv;
- int status;
-};
-
-
-static int pmain (lua_State *L) {
- struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
- char **argv = s->argv;
- int script;
- int has_i = 0, has_v = 0, has_e = 0;
- globalL = L;
- if (argv[0] && argv[0][0]) progname = argv[0];
- lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
- luaL_openlibs(L); /* open libraries */
- lua_gc(L, LUA_GCRESTART, 0);
- s->status = handle_luainit(L);
- if (s->status != 0) return 0;
- script = collectargs(argv, &has_i, &has_v, &has_e);
- if (script < 0) { /* invalid args? */
- print_usage();
- s->status = 1;
- return 0;
- }
- if (has_v) print_version();
- s->status = runargs(L, argv, (script > 0) ? script : s->argc);
- if (s->status != 0) return 0;
- if (script)
- s->status = handle_script(L, argv, script);
- if (s->status != 0) return 0;
- if (has_i)
- dotty(L);
- else if (script == 0 && !has_e && !has_v) {
- if (lua_stdin_is_tty()) {
- print_version();
- dotty(L);
- }
- else dofile(L, NULL); /* executes stdin as a file */
- }
- return 0;
-}
-
-
-int main (int argc, char **argv) {
- int status;
- struct Smain s;
- lua_State *L = lua_open(); /* create state */
- if (L == NULL) {
- l_message(argv[0], "cannot create state: not enough memory");
- return EXIT_FAILURE;
- }
- s.argc = argc;
- s.argv = argv;
- status = lua_cpcall(L, &pmain, &s);
- report(L, status);
- lua_close(L);
- return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
-}
-
diff --git a/engines/sword25/util/lua/lua.h b/engines/sword25/util/lua/lua.h
index 5bc97b746f..088a511cf9 100644
--- a/engines/sword25/util/lua/lua.h
+++ b/engines/sword25/util/lua/lua.h
@@ -1,5 +1,5 @@
/*
-** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $
+** $Id$
** Lua - An Extensible Extension Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
diff --git a/engines/sword25/util/lua/luac.c b/engines/sword25/util/lua/luac.c
deleted file mode 100644
index d07017391b..0000000000
--- a/engines/sword25/util/lua/luac.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
-** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $
-** Lua compiler (saves bytecodes to files; also list bytecodes)
-** See Copyright Notice in lua.h
-*/
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define luac_c
-#define LUA_CORE
-
-#include "lua.h"
-#include "lauxlib.h"
-
-#include "ldo.h"
-#include "lfunc.h"
-#include "lmem.h"
-#include "lobject.h"
-#include "lopcodes.h"
-#include "lstring.h"
-#include "lundump.h"
-
-#define PROGNAME "luac" /* default program name */
-#define OUTPUT PROGNAME ".out" /* default output file */
-
-static int listing=0; /* list bytecodes? */
-static int dumping=1; /* dump bytecodes? */
-static int stripping=0; /* strip debug information? */
-static char Output[]={ OUTPUT }; /* default output file name */
-static const char* output=Output; /* actual output file name */
-static const char* progname=PROGNAME; /* actual program name */
-
-static void fatal(const char* message)
-{
- fprintf(stderr,"%s: %s\n",progname,message);
- exit(EXIT_FAILURE);
-}
-
-static void cannot(const char* what)
-{
- fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
- exit(EXIT_FAILURE);
-}
-
-static void usage(const char* message)
-{
- if (*message=='-')
- fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
- else
- fprintf(stderr,"%s: %s\n",progname,message);
- fprintf(stderr,
- "usage: %s [options] [filenames].\n"
- "Available options are:\n"
- " - process stdin\n"
- " -l list\n"
- " -o name output to file " LUA_QL("name") " (default is \"%s\")\n"
- " -p parse only\n"
- " -s strip debug information\n"
- " -v show version information\n"
- " -- stop handling options\n",
- progname,Output);
- exit(EXIT_FAILURE);
-}
-
-#define IS(s) (strcmp(argv[i],s)==0)
-
-static int doargs(int argc, char* argv[])
-{
- int i;
- int version=0;
- if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
- for (i=1; i<argc; i++)
- {
- if (*argv[i]!='-') /* end of options; keep it */
- break;
- else if (IS("--")) /* end of options; skip it */
- {
- ++i;
- if (version) ++version;
- break;
- }
- else if (IS("-")) /* end of options; use stdin */
- break;
- else if (IS("-l")) /* list */
- ++listing;
- else if (IS("-o")) /* output file */
- {
- output=argv[++i];
- if (output==NULL || *output==0) usage(LUA_QL("-o") " needs argument");
- if (IS("-")) output=NULL;
- }
- else if (IS("-p")) /* parse only */
- dumping=0;
- else if (IS("-s")) /* strip debug information */
- stripping=1;
- else if (IS("-v")) /* show version */
- ++version;
- else /* unknown option */
- usage(argv[i]);
- }
- if (i==argc && (listing || !dumping))
- {
- dumping=0;
- argv[--i]=Output;
- }
- if (version)
- {
- printf("%s %s\n",LUA_RELEASE,LUA_COPYRIGHT);
- if (version==argc-1) exit(EXIT_SUCCESS);
- }
- return i;
-}
-
-#define toproto(L,i) (clvalue(L->top+(i))->l.p)
-
-static const Proto* combine(lua_State* L, int n)
-{
- if (n==1)
- return toproto(L,-1);
- else
- {
- int i,pc;
- Proto* f=luaF_newproto(L);
- setptvalue2s(L,L->top,f); incr_top(L);
- f->source=luaS_newliteral(L,"=(" PROGNAME ")");
- f->maxstacksize=1;
- pc=2*n+1;
- f->code=luaM_newvector(L,pc,Instruction);
- f->sizecode=pc;
- f->p=luaM_newvector(L,n,Proto*);
- f->sizep=n;
- pc=0;
- for (i=0; i<n; i++)
- {
- f->p[i]=toproto(L,i-n-1);
- f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);
- f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);
- }
- f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);
- return f;
- }
-}
-
-static int writer(lua_State* L, const void* p, size_t size, void* u)
-{
- UNUSED(L);
- return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
-}
-
-struct Smain {
- int argc;
- char** argv;
-};
-
-static int pmain(lua_State* L)
-{
- struct Smain* s = (struct Smain*)lua_touserdata(L, 1);
- int argc=s->argc;
- char** argv=s->argv;
- const Proto* f;
- int i;
- if (!lua_checkstack(L,argc)) fatal("too many input files");
- for (i=0; i<argc; i++)
- {
- const char* filename=IS("-") ? NULL : argv[i];
- if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1));
- }
- f=combine(L,argc);
- if (listing) luaU_print(f,listing>1);
- if (dumping)
- {
- FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
- if (D==NULL) cannot("open");
- lua_lock(L);
- luaU_dump(L,f,writer,D,stripping);
- lua_unlock(L);
- if (ferror(D)) cannot("write");
- if (fclose(D)) cannot("close");
- }
- return 0;
-}
-
-int main(int argc, char* argv[])
-{
- lua_State* L;
- struct Smain s;
- int i=doargs(argc,argv);
- argc-=i; argv+=i;
- if (argc<=0) usage("no input files given");
- L=lua_open();
- if (L==NULL) fatal("not enough memory for state");
- s.argc=argc;
- s.argv=argv;
- if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1));
- lua_close(L);
- return EXIT_SUCCESS;
-}
diff --git a/engines/sword25/util/lua/luaconf.h b/engines/sword25/util/lua/luaconf.h
index d4eb2c9cd4..fa565c7697 100644
--- a/engines/sword25/util/lua/luaconf.h
+++ b/engines/sword25/util/lua/luaconf.h
@@ -1,5 +1,5 @@
/*
-** $Id: luaconf.h,v 1.82.1.6 2008/01/18 17:07:48 roberto Exp $
+** $Id$
** Configuration file for Lua
** See Copyright Notice in lua.h
*/
@@ -319,7 +319,7 @@
** CHANGE it (define it) if you want exact compatibility with the
** behavior of setn/getn in Lua 5.0.
*/
-#define LUA_COMPAT_GETN // BS25 #undef LUA_COMPAT_GETN
+#define LUA_COMPAT_GETN /* BS25 #undef LUA_COMPAT_GETN */
/*
@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib.
diff --git a/engines/sword25/util/lua/lualib.h b/engines/sword25/util/lua/lualib.h
index 469417f670..33d4e314c2 100644
--- a/engines/sword25/util/lua/lualib.h
+++ b/engines/sword25/util/lua/lualib.h
@@ -1,5 +1,5 @@
/*
-** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Lua standard libraries
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lundump.c b/engines/sword25/util/lua/lundump.cpp
index 731c064553..4ffc623575 100644
--- a/engines/sword25/util/lua/lundump.c
+++ b/engines/sword25/util/lua/lundump.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lundump.c,v 2.7.1.2 2008/01/18 16:39:11 roberto Exp $
+** $Id$
** load precompiled Lua chunks
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lundump.h b/engines/sword25/util/lua/lundump.h
index c80189dbff..f791a4f173 100644
--- a/engines/sword25/util/lua/lundump.h
+++ b/engines/sword25/util/lua/lundump.h
@@ -1,5 +1,5 @@
/*
-** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** load precompiled Lua chunks
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lvm.c b/engines/sword25/util/lua/lvm.cpp
index ee3256ab94..ae70fe2645 100644
--- a/engines/sword25/util/lua/lvm.c
+++ b/engines/sword25/util/lua/lvm.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $
+** $Id$
** Lua virtual machine
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lvm.h b/engines/sword25/util/lua/lvm.h
index bfe4f5678d..dff2a139f7 100644
--- a/engines/sword25/util/lua/lvm.h
+++ b/engines/sword25/util/lua/lvm.h
@@ -1,5 +1,5 @@
/*
-** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Lua virtual machine
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lzio.c b/engines/sword25/util/lua/lzio.cpp
index 293edd59b0..e1e7b28a29 100644
--- a/engines/sword25/util/lua/lzio.c
+++ b/engines/sword25/util/lua/lzio.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** a generic input stream interface
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/lzio.h b/engines/sword25/util/lua/lzio.h
index 51d695d8c1..9aa9e4b537 100644
--- a/engines/sword25/util/lua/lzio.h
+++ b/engines/sword25/util/lua/lzio.h
@@ -1,5 +1,5 @@
/*
-** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Buffered streams
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/lua/print.c b/engines/sword25/util/lua/print.cpp
index e240cfc3c6..22039c9861 100644
--- a/engines/sword25/util/lua/print.c
+++ b/engines/sword25/util/lua/print.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $
+** $Id$
** print bytecodes
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep.c b/engines/sword25/util/pluto/pdep.cpp
index a32c43b42d..a32c43b42d 100644
--- a/engines/sword25/util/pluto/pdep.c
+++ b/engines/sword25/util/pluto/pdep.cpp
diff --git a/engines/sword25/util/pluto/pdep/lauxlib.h b/engines/sword25/util/pluto/pdep/lauxlib.h
index 34258235db..d58f290527 100644
--- a/engines/sword25/util/pluto/pdep/lauxlib.h
+++ b/engines/sword25/util/pluto/pdep/lauxlib.h
@@ -1,5 +1,5 @@
/*
-** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/ldo.h b/engines/sword25/util/pluto/pdep/ldo.h
index 98fddac59f..4c97134805 100644
--- a/engines/sword25/util/pluto/pdep/ldo.h
+++ b/engines/sword25/util/pluto/pdep/ldo.h
@@ -1,5 +1,5 @@
/*
-** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lfunc.h b/engines/sword25/util/pluto/pdep/lfunc.h
index a68cf5151c..4c2b7fd138 100644
--- a/engines/sword25/util/pluto/pdep/lfunc.h
+++ b/engines/sword25/util/pluto/pdep/lfunc.h
@@ -1,5 +1,5 @@
/*
-** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lgc.h b/engines/sword25/util/pluto/pdep/lgc.h
index 5a8dc605b3..5123ccb479 100644
--- a/engines/sword25/util/pluto/pdep/lgc.h
+++ b/engines/sword25/util/pluto/pdep/lgc.h
@@ -1,5 +1,5 @@
/*
-** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Garbage Collector
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/llimits.h b/engines/sword25/util/pluto/pdep/llimits.h
index ca8dcb7224..a31ad160ad 100644
--- a/engines/sword25/util/pluto/pdep/llimits.h
+++ b/engines/sword25/util/pluto/pdep/llimits.h
@@ -1,5 +1,5 @@
/*
-** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lobject.h b/engines/sword25/util/pluto/pdep/lobject.h
index e7199dfc68..35aaed028a 100644
--- a/engines/sword25/util/pluto/pdep/lobject.h
+++ b/engines/sword25/util/pluto/pdep/lobject.h
@@ -1,5 +1,5 @@
/*
-** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lopcodes.h b/engines/sword25/util/pluto/pdep/lopcodes.h
index 41224d6ee1..e1aed0f637 100644
--- a/engines/sword25/util/pluto/pdep/lopcodes.h
+++ b/engines/sword25/util/pluto/pdep/lopcodes.h
@@ -1,5 +1,5 @@
/*
-** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lstate.h b/engines/sword25/util/pluto/pdep/lstate.h
index 3bc575b6bc..94a6249461 100644
--- a/engines/sword25/util/pluto/pdep/lstate.h
+++ b/engines/sword25/util/pluto/pdep/lstate.h
@@ -1,5 +1,5 @@
/*
-** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** $Id$
** Global State
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lstring.h b/engines/sword25/util/pluto/pdep/lstring.h
index 73a2ff8b38..c88e4c12a9 100644
--- a/engines/sword25/util/pluto/pdep/lstring.h
+++ b/engines/sword25/util/pluto/pdep/lstring.h
@@ -1,5 +1,5 @@
/*
-** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** String table (keep all strings handled by Lua)
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/ltm.h b/engines/sword25/util/pluto/pdep/ltm.h
index 64343b781b..1b89683ef3 100644
--- a/engines/sword25/util/pluto/pdep/ltm.h
+++ b/engines/sword25/util/pluto/pdep/ltm.h
@@ -1,5 +1,5 @@
/*
-** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Tag methods
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pdep/lua.h b/engines/sword25/util/pluto/pdep/lua.h
index 0f3f28fce5..68dd887f0f 100644
--- a/engines/sword25/util/pluto/pdep/lua.h
+++ b/engines/sword25/util/pluto/pdep/lua.h
@@ -1,5 +1,5 @@
/*
-** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $
+** $Id$
** Lua - An Extensible Extension Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h
index 4e654a52c9..2f167d7d58 100644
--- a/engines/sword25/util/pluto/pdep/lzio.h
+++ b/engines/sword25/util/pluto/pdep/lzio.h
@@ -1,5 +1,5 @@
/*
-** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** Buffered streams
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pluto.c b/engines/sword25/util/pluto/pluto.cpp
index 61eb40e984..957f5af795 100644
--- a/engines/sword25/util/pluto/pluto.c
+++ b/engines/sword25/util/pluto/pluto.cpp
@@ -624,7 +624,12 @@ static void persist(PersistInfo *pi)
if(!lua_isnil(pi->L, -1)) {
/* perms reftbl ... obj ref */
int zero = 0;
- int ref = (int)lua_touserdata(pi->L, -1);
+ // FIXME: Casting a pointer to an integer data type is a bad idea we
+ // should really get rid of this by fixing the design of this code.
+ // For now casting to size_t should silence most (all?) compilers,
+ // since size_t is supposedly the same size as a pointer on most
+ // (modern) architectures.
+ int ref = (int)(size_t)lua_touserdata(pi->L, -1);
pi->writer(pi->L, &zero, sizeof(int), pi->ud);
pi->writer(pi->L, &ref, sizeof(int), pi->ud);
lua_pop(pi->L, 1);
@@ -1470,6 +1475,7 @@ static void unpersistpermanent(int ref, UnpersistInfo *upi)
/* perms reftbl perm */
}
+#if 0
/* For debugging only; not called when lua_assert is empty */
static int inreftable(lua_State *L, int ref)
{
@@ -1485,6 +1491,7 @@ static int inreftable(lua_State *L, int ref)
/* perms reftbl ... */
return res;
}
+#endif
static void unpersist(UnpersistInfo *upi)
{
diff --git a/engines/sword25/util/pluto/plzio.c b/engines/sword25/util/pluto/plzio.cpp
index 7c5ab3b773..0efc3dfcf2 100644
--- a/engines/sword25/util/pluto/plzio.c
+++ b/engines/sword25/util/pluto/plzio.cpp
@@ -1,5 +1,5 @@
/*
-** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** $Id$
** a generic input stream interface
** See Copyright Notice in lua.h
*/
diff --git a/engines/sword25/util/pluto/pptest.c b/engines/sword25/util/pluto/pptest.cpp
index 1bfecf2b75..1bfecf2b75 100644
--- a/engines/sword25/util/pluto/pptest.c
+++ b/engines/sword25/util/pluto/pptest.cpp
diff --git a/engines/sword25/util/pluto/puptest.c b/engines/sword25/util/pluto/puptest.cpp
index e9aa7ea305..e9aa7ea305 100644
--- a/engines/sword25/util/pluto/puptest.c
+++ b/engines/sword25/util/pluto/puptest.cpp
diff --git a/engines/teenagent/animation.cpp b/engines/teenagent/animation.cpp
index ce1fef009e..56812001e8 100644
--- a/engines/teenagent/animation.cpp
+++ b/engines/teenagent/animation.cpp
@@ -112,7 +112,7 @@ void Animation::load(Common::SeekableReadStream *s, Type type) {
//fixme: do not reload the same animation each time
free();
- if (s == NULL && s->size() <= 1) {
+ if (s == NULL || s->size() <= 1) {
debug(1, "empty animation");
return;
}
diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp
index a2dab9658d..d741039e8d 100644
--- a/engines/teenagent/detection.cpp
+++ b/engines/teenagent/detection.cpp
@@ -83,16 +83,27 @@ static const ADGameDescription teenAgentGameDescriptions[] = {
};
static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
(const byte *)teenAgentGameDescriptions,
+ // Size of that superset structure
sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
5000,
+ // List of all engine targets
teenAgentGames,
+ // Structure for autoupgrading obsolete targets
0,
+ // Name of single gameid (optional)
"teenagent",
+ // List of files for file-based fallback detection (optional)
0,
+ // Flags
0,
+ // Additional GUI options (for every game}
Common::GUIO_NONE,
+ // Maximum directory depth
1,
+ // List of directory globs
0
};
@@ -137,7 +148,7 @@ public:
// }
static Common::String generateGameStateFileName(const char *target, int slot) {
- return Common::String::printf("%s.%02d", target, slot);
+ return Common::String::format("%s.%02d", target, slot);
}
virtual SaveStateList listSaves(const char *target) const {
diff --git a/engines/teenagent/inventory.cpp b/engines/teenagent/inventory.cpp
index 47267dabf0..16a5f20ee6 100644
--- a/engines/teenagent/inventory.cpp
+++ b/engines/teenagent/inventory.cpp
@@ -22,7 +22,7 @@
* $Id$
*/
-#include "common/stream.h"
+#include "common/memstream.h"
#include "common/ptr.h"
#include "teenagent/inventory.h"
diff --git a/engines/teenagent/objects.cpp b/engines/teenagent/objects.cpp
index f8df4fee77..e6442d3809 100644
--- a/engines/teenagent/objects.cpp
+++ b/engines/teenagent/objects.cpp
@@ -23,7 +23,7 @@
*/
#include "common/debug.h"
-#include "common/stream.h"
+#include "common/memstream.h"
#include "teenagent/objects.h"
#include "teenagent/resources.h"
diff --git a/engines/teenagent/pack.cpp b/engines/teenagent/pack.cpp
index e3b7a33960..8584e05807 100644
--- a/engines/teenagent/pack.cpp
+++ b/engines/teenagent/pack.cpp
@@ -25,6 +25,8 @@
#include "teenagent/pack.h"
#include "common/util.h"
#include "common/debug.h"
+#include "common/memstream.h"
+#include "common/substream.h"
namespace TeenAgent {
diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp
index f1527fc78d..2f3bd21ae4 100644
--- a/engines/teenagent/teenagent.cpp
+++ b/engines/teenagent/teenagent.cpp
@@ -28,6 +28,8 @@
#include "common/savefile.h"
#include "common/system.h"
+#include "backends/audiocd/audiocd.h"
+
#include "engines/advancedDetector.h"
#include "engines/util.h"
@@ -196,9 +198,9 @@ void TeenAgentEngine::deinit() {
Common::Error TeenAgentEngine::loadGameState(int slot) {
debug(0, "loading from slot %d", slot);
Common::ScopedPtr<Common::InSaveFile>
- in(_saveFileMan->openForLoading(Common::String::printf("teenagent.%02d", slot)));
+ in(_saveFileMan->openForLoading(Common::String::format("teenagent.%02d", slot)));
if (!in)
- in.reset(_saveFileMan->openForLoading(Common::String::printf("teenagent.%d", slot)));
+ in.reset(_saveFileMan->openForLoading(Common::String::format("teenagent.%d", slot)));
if (!in)
return Common::kReadPermissionDenied;
@@ -231,7 +233,7 @@ Common::Error TeenAgentEngine::loadGameState(int slot) {
Common::Error TeenAgentEngine::saveGameState(int slot, const char *desc) {
debug(0, "saving to slot %d", slot);
- Common::ScopedPtr<Common::OutSaveFile> out(_saveFileMan->openForSaving(Common::String::printf("teenagent.%02d", slot)));
+ Common::ScopedPtr<Common::OutSaveFile> out(_saveFileMan->openForSaving(Common::String::format("teenagent.%02d", slot)));
if (!out)
return Common::kWritingFailed;
diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h
index dc195c0f4e..069e6e40d0 100644
--- a/engines/teenagent/teenagent.h
+++ b/engines/teenagent/teenagent.h
@@ -40,7 +40,7 @@ struct ADGameDescription;
*
* Status of this engine: Complete
*
- * Supported games:
+ * Games using this engine:
* - Teen Agent
*/
namespace TeenAgent {
diff --git a/engines/testbed/config-params.cpp b/engines/testbed/config-params.cpp
index e5a581ec29..c9f3932539 100644
--- a/engines/testbed/config-params.cpp
+++ b/engines/testbed/config-params.cpp
@@ -27,7 +27,7 @@
#include "testbed/config-params.h"
-DECLARE_SINGLETON(Testbed::ConfigParams)
+DECLARE_SINGLETON(Testbed::ConfigParams);
namespace Testbed {
diff --git a/engines/testbed/config.cpp b/engines/testbed/config.cpp
index fe34910204..4f871db8d2 100644
--- a/engines/testbed/config.cpp
+++ b/engines/testbed/config.cpp
@@ -246,11 +246,11 @@ void TestbedConfigManager::parseConfigFile() {
int TestbedConfigManager::getNumSuitesEnabled() {
int count = 0;
for (uint i = 0; i < _testsuiteList.size(); i++) {
- if (_testsuiteList[i]->isEnabled()) {
- count++;
+ if (_testsuiteList[i]->isEnabled()) {
+ count++;
}
- }
- return count;
+ }
+ return count;
}
Testsuite *TestbedConfigManager::getTestsuiteByName(const Common::String &name) {
diff --git a/engines/testbed/config.h b/engines/testbed/config.h
index 2ee5b09002..cf1948b412 100644
--- a/engines/testbed/config.h
+++ b/engines/testbed/config.h
@@ -31,7 +31,7 @@
#include "common/str-array.h"
#include "common/tokenizer.h"
-#include "gui/ListWidget.h"
+#include "gui/widgets/list.h"
#include "gui/options.h"
#include "gui/ThemeEngine.h"
diff --git a/engines/testbed/detection.cpp b/engines/testbed/detection.cpp
index 1b8a86cea6..734ed0c22a 100644
--- a/engines/testbed/detection.cpp
+++ b/engines/testbed/detection.cpp
@@ -51,16 +51,27 @@ static const ADGameDescription testbedDescriptions[] = {
};
static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
(const byte *)testbedDescriptions,
+ // Size of that superset structure
sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
512,
+ // List of all engine targets
testbed_setting,
+ // Structure for autoupgrading obsolete targets
0,
+ // Name of single gameid (optional)
"testbed",
+ // List of files for file-based fallback detection (optional)
0,
+ // Flags
ADGF_NO_FLAGS,
+ // Additional GUI options (for every game}
Common::GUIO_NONE,
+ // Maximum directory depth
1,
+ // List of directory globs
0
};
diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp
index 086db21c67..079239c920 100644
--- a/engines/testbed/graphics.cpp
+++ b/engines/testbed/graphics.cpp
@@ -769,7 +769,7 @@ TestExitStatus GFXtests::scaledCursors() {
// Switch Graphics mode
// FIXME: Crashes with "3x" mode now.:
- info = Common::String::printf("Testing : Scaled cursors with GFX Mode %s\n", gfxMode->name);
+ info = Common::String::format("Testing : Scaled cursors with GFX Mode %s\n", gfxMode->name);
if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
Testsuite::logPrintf("\tInfo! Skipping sub-test : Scaled Cursors :: GFX Mode %s\n", gfxMode->name);
gfxMode++;
@@ -1104,7 +1104,7 @@ TestExitStatus GFXtests::pixelFormats() {
Common::Point pt(0, 170);
Common::String msg;
- msg = Common::String::printf("Testing Pixel Formats, %d of %d", numFormatsTested, pfList.size());
+ msg = Common::String::format("Testing Pixel Formats, %d of %d", numFormatsTested, pfList.size());
Testsuite::writeOnScreen(msg, pt, true);
// CopyRectToScreen could have been used, but that may involve writing code which
diff --git a/engines/testbed/midi.cpp b/engines/testbed/midi.cpp
index 0ec2678d47..499c44e50a 100644
--- a/engines/testbed/midi.cpp
+++ b/engines/testbed/midi.cpp
@@ -24,6 +24,7 @@
#include "common/archive.h"
#include "common/events.h"
+#include "common/memstream.h"
#include "graphics/cursorman.h"
@@ -34,7 +35,7 @@
namespace Testbed {
-bool MidiTests::loadMusicInMemory(Common::MemoryWriteStreamDynamic *ws) {
+bool MidiTests::loadMusicInMemory(Common::WriteStream *ws) {
Common::SeekableReadStream *midiFile = SearchMan.createReadStreamForMember("music.mid");
if (!midiFile) {
Testsuite::logPrintf("Error! Can't open Midi music file, check game data directory for file music.mid\n");
diff --git a/engines/testbed/midi.h b/engines/testbed/midi.h
index c73d937834..0fb5cf80d5 100644
--- a/engines/testbed/midi.h
+++ b/engines/testbed/midi.h
@@ -36,7 +36,7 @@ namespace Testbed {
namespace MidiTests {
// Helper functions for MIDI tests
-bool loadMusicInMemory(Common::MemoryWriteStreamDynamic *ws);
+bool loadMusicInMemory(Common::WriteStream *ws);
void waitForMusicToPlay(MidiParser *parser);
// will contain function declarations for MIDI tests
diff --git a/engines/testbed/misc.cpp b/engines/testbed/misc.cpp
index 2159974c51..36687f570e 100644
--- a/engines/testbed/misc.cpp
+++ b/engines/testbed/misc.cpp
@@ -28,7 +28,7 @@
namespace Testbed {
Common::String MiscTests::getHumanReadableFormat(TimeDate &td) {
- return Common::String::printf("%d:%d:%d on %d/%d/%d (dd/mm/yyyy)", td.tm_hour, td.tm_min, td.tm_sec, td.tm_mday, td.tm_mon + 1, td.tm_year + 1900);
+ return Common::String::format("%d:%d:%d on %d/%d/%d (dd/mm/yyyy)", td.tm_hour, td.tm_min, td.tm_sec, td.tm_mday, td.tm_mon + 1, td.tm_year + 1900);
}
void MiscTests::timerCallback(void *arg) {
diff --git a/engines/testbed/sound.cpp b/engines/testbed/sound.cpp
index e256621553..8354e7bad0 100644
--- a/engines/testbed/sound.cpp
+++ b/engines/testbed/sound.cpp
@@ -22,9 +22,10 @@
* $Id$
*/
-#include "sound/audiocd.h"
#include "sound/softsynth/pcspk.h"
+#include "backends/audiocd/audiocd.h"
+
#include "testbed/sound.h"
namespace Testbed {
@@ -190,17 +191,17 @@ TestExitStatus SoundSubsystem::audiocdOutput() {
// Play all tracks
for (int i = 1; i < 5; i++) {
- AudioCD.play(i, 1, 0, 0);
- while (AudioCD.isPlaying()) {
+ g_system->getAudioCDManager()->play(i, 1, 0, 0);
+ while (g_system->getAudioCDManager()->isPlaying()) {
g_system->delayMillis(500);
- Testsuite::writeOnScreen(Common::String::printf("Playing Now: track%02d", i), pt);
+ Testsuite::writeOnScreen(Common::String::format("Playing Now: track%02d", i), pt);
}
g_system->delayMillis(500);
}
Testsuite::clearScreen();
if (Testsuite::handleInteractiveInput("Were all the tracks played in order i.e 1-2-3-last ?", "Yes", "No", kOptionRight)) {
- Testsuite::logPrintf("Error! Error in AudioCD.play() or probably sound files were not detected, try -d1 (debuglevel 1)\n");
+ Testsuite::logPrintf("Error! Error in _system->getAudioCDManager()->play() or probably sound files were not detected, try -d1 (debuglevel 1)\n");
passed = kTestFailed;
}
@@ -234,19 +235,19 @@ TestExitStatus SoundSubsystem::sampleRates() {
Common::Point pt(0, 100);
mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, s1);
- Testsuite::writeOnScreen(Common::String::printf("Playing at smaple rate: %d", s1->getRate()), pt);
+ Testsuite::writeOnScreen(Common::String::format("Playing at smaple rate: %d", s1->getRate()), pt);
g_system->delayMillis(1000);
mixer->stopHandle(handle);
g_system->delayMillis(1000);
mixer->playStream(Audio::Mixer::kSpeechSoundType, &handle, s2);
- Testsuite::writeOnScreen(Common::String::printf("Playing at sample rate : %d", s2->getRate()), pt);
+ Testsuite::writeOnScreen(Common::String::format("Playing at sample rate : %d", s2->getRate()), pt);
g_system->delayMillis(1000);
mixer->stopHandle(handle);
g_system->delayMillis(1000);
mixer->playStream(Audio::Mixer::kSFXSoundType, &handle, s3);
- Testsuite::writeOnScreen(Common::String::printf("Playing at sample rate : %d", s3->getRate()), pt);
+ Testsuite::writeOnScreen(Common::String::format("Playing at sample rate : %d", s3->getRate()), pt);
g_system->delayMillis(1000);
mixer->stopHandle(handle);
g_system->delayMillis(1000);
diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp
index 071fba8c2c..1ceecb8ebf 100644
--- a/engines/testbed/testbed.cpp
+++ b/engines/testbed/testbed.cpp
@@ -48,10 +48,10 @@ void TestbedExitDialog::init() {
GUI::ListWidget::ColorList colors;
for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i != _testsuiteList.end(); ++i) {
- strArray.push_back(Common::String::printf("%s :", (*i)->getDescription()));
+ strArray.push_back(Common::String::format("%s :", (*i)->getDescription()));
colors.push_back(GUI::ThemeEngine::kFontColorNormal);
if ((*i)->isEnabled()) {
- strArray.push_back(Common::String::printf("Passed: %d Failed: %d Skipped: %d", (*i)->getNumTestsPassed(), (*i)->getNumTestsFailed(), (*i)->getNumTestsSkipped()));
+ strArray.push_back(Common::String::format("Passed: %d Failed: %d Skipped: %d", (*i)->getNumTestsPassed(), (*i)->getNumTestsFailed(), (*i)->getNumTestsSkipped()));
} else {
strArray.push_back("Skipped");
}
diff --git a/engines/testbed/testsuite.cpp b/engines/testbed/testsuite.cpp
index 8cb9ffe309..fa8764e869 100644
--- a/engines/testbed/testsuite.cpp
+++ b/engines/testbed/testsuite.cpp
@@ -233,7 +233,7 @@ uint Testsuite::parseEvents() {
}
void Testsuite::updateStats(const char *prefix, const char *info, uint testNum, uint numTests, Common::Point pt) {
- Common::String text = Common::String::printf(" Running %s: %s (%d of %d) ", prefix, info, testNum, numTests);
+ Common::String text = Common::String::format(" Running %s: %s (%d of %d) ", prefix, info, testNum, numTests);
writeOnScreen(text, pt);
uint barColor = kColorSpecial;
// below the text a rectangle denoting the progress in the testsuite can be drawn.
diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp
index 3577f4e0cc..7a1f9fbf55 100644
--- a/engines/tinsel/actors.cpp
+++ b/engines/tinsel/actors.cpp
@@ -79,6 +79,8 @@ struct T2_ACTOR_STRUC {
#define MAX_REELS 6
+// FIXME: Avoid non-const global vars
+
static int LeadActorId = 0; // The lead actor
static int NumActors = 0; // The total number of actors in the game
@@ -199,6 +201,10 @@ void RegisterActors(int num) {
void FreeActors() {
free(actorInfo);
actorInfo = NULL;
+ if (TinselV2) {
+ free(zFactors);
+ zFactors = NULL;
+ }
}
/**
@@ -625,7 +631,7 @@ int NextTaggedActor() {
PMOVER pActor;
bool hid;
- do {
+ while (ti < NumActors) {
if (actorInfo[ti].tagged) {
pActor = GetMover(ti+1);
if (pActor)
@@ -637,7 +643,8 @@ int NextTaggedActor() {
return ++ti;
}
}
- } while (++ti < NumActors);
+ ++ti;
+ }
return 0;
}
diff --git a/engines/tinsel/background.cpp b/engines/tinsel/background.cpp
index abfb9692a9..560216aadb 100644
--- a/engines/tinsel/background.cpp
+++ b/engines/tinsel/background.cpp
@@ -34,8 +34,10 @@
namespace Tinsel {
+// FIXME: Avoid non-const global vars
+
// current background
-BACKGND *pCurBgnd = NULL;
+const BACKGND *pCurBgnd = NULL;
// FIXME: Not yet used
static bool bEntireRedraw;
@@ -45,7 +47,7 @@ static bool bEntireRedraw;
* @param pBgnd Pointer to data struct for current background
*/
-void InitBackground(BACKGND *pBgnd) {
+void InitBackground(const BACKGND *pBgnd) {
int i; // playfield counter
PLAYFIELD *pPlayfield; // pointer to current playfield
diff --git a/engines/tinsel/background.h b/engines/tinsel/background.h
index 747e51a8f1..81b490488e 100644
--- a/engines/tinsel/background.h
+++ b/engines/tinsel/background.h
@@ -77,7 +77,7 @@ struct BACKGND {
\*----------------------------------------------------------------------*/
void InitBackground( // called to initialise a background
- BACKGND *pBgnd); // pointer to data struct for current background
+ const BACKGND *pBgnd); // pointer to data struct for current background
void StartupBackground(CORO_PARAM, SCNHANDLE hFilm);
diff --git a/engines/tinsel/bg.cpp b/engines/tinsel/bg.cpp
index efd4b644cf..68653b16f4 100644
--- a/engines/tinsel/bg.cpp
+++ b/engines/tinsel/bg.cpp
@@ -48,6 +48,7 @@ namespace Tinsel {
#define MAX_BG 10
+// FIXME: Avoid non-const global vars
static SCNHANDLE hBgPal = 0; // Background's palette
static POBJECT pBG[MAX_BG];
static ANIM thisAnim[MAX_BG]; // used by BGmainProcess()
diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp
index 2077789b9c..f74f3e34c3 100644
--- a/engines/tinsel/bmv.cpp
+++ b/engines/tinsel/bmv.cpp
@@ -514,7 +514,7 @@ void BMVPlayer::MovieText(CORO_PARAM, int stringId, int x, int y, int fontId, CO
LoadSubString(stringId, 0, TextBufferAddr(), TBUFSZ);
texts[index].dieFrame = currentFrame + duration;
- texts[index].pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+ texts[index].pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(),
0,
x, y,
@@ -1050,7 +1050,8 @@ void BMVPlayer::CopyMovieToScreen() {
*/
void BMVPlayer::LookAtBuffers() {
// FIXME: What's the point of this function???
- static int junk;
+ // Maybe to ensure the relevant data is loaded into cache by the CPU?
+ static int junk; // FIXME: Avoid non-const global vars
int i;
if (bigBuffer) {
diff --git a/engines/tinsel/coroutine.cpp b/engines/tinsel/coroutine.cpp
new file mode 100644
index 0000000000..b568cb4a46
--- /dev/null
+++ b/engines/tinsel/coroutine.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.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "tinsel/coroutine.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+namespace Tinsel {
+
+
+CoroContext nullContext = NULL; // FIXME: Avoid non-const global vars
+
+
+#if COROUTINE_DEBUG
+namespace {
+static int s_coroCount = 0;
+
+typedef Common::HashMap<Common::String, int> CoroHashMap;
+static CoroHashMap *s_coroFuncs = 0;
+
+static void changeCoroStats(const char *func, int change) {
+ if (!s_coroFuncs)
+ s_coroFuncs = new CoroHashMap();
+
+ (*s_coroFuncs)[func] += change;
+}
+
+static void displayCoroStats() {
+ debug("%d active coros", s_coroCount);
+
+ // Loop over s_coroFuncs and print info about active coros
+ if (!s_coroFuncs)
+ return;
+ for (CoroHashMap::const_iterator it = s_coroFuncs->begin();
+ it != s_coroFuncs->end(); ++it) {
+ if (it->_value != 0)
+ debug(" %3d x %s", it->_value, it->_key.c_str());
+ }
+}
+
+}
+#endif
+
+CoroBaseContext::CoroBaseContext(const char *func)
+ : _line(0), _sleep(0), _subctx(0) {
+#if COROUTINE_DEBUG
+ _funcName = func;
+ changeCoroStats(_funcName, +1);
+ s_coroCount++;
+#endif
+}
+
+CoroBaseContext::~CoroBaseContext() {
+#if COROUTINE_DEBUG
+ s_coroCount--;
+ changeCoroStats(_funcName, -1);
+ debug("Deleting coro in %s at %p (subctx %p)",
+ _funcName, (void *)this, (void *)_subctx);
+ displayCoroStats();
+#endif
+ delete _subctx;
+}
+
+} // End of namespace Tinsel
+
diff --git a/engines/tinsel/coroutine.h b/engines/tinsel/coroutine.h
index d4cd54a8db..82bc2c4cca 100644
--- a/engines/tinsel/coroutine.h
+++ b/engines/tinsel/coroutine.h
@@ -27,6 +27,7 @@
#define TINSEL_COROUTINE_H
#include "common/scummsys.h"
+#include "common/util.h" // for SCUMMVM_CURRENT_FUNCTION
namespace Tinsel {
@@ -58,6 +59,10 @@ namespace Tinsel {
*/
//@{
+
+// Enable this macro to enable some debugging support in the coroutine code.
+//#define COROUTINE_DEBUG 1
+
/**
* The core of any coroutine context which captures the 'state' of a coroutine.
* Private use only.
@@ -66,8 +71,11 @@ struct CoroBaseContext {
int _line;
int _sleep;
CoroBaseContext *_subctx;
- CoroBaseContext() : _line(0), _sleep(0), _subctx(0) {}
- ~CoroBaseContext() { delete _subctx; }
+#if COROUTINE_DEBUG
+ const char *_funcName;
+#endif
+ CoroBaseContext(const char *func);
+ ~CoroBaseContext();
};
typedef CoroBaseContext *CoroContext;
@@ -101,9 +109,7 @@ public:
};
-#define CORO_PARAM CoroContext &coroParam
-
-#define CORO_SUBCTX coroParam->_subctx
+#define CORO_PARAM CoroContext &coroParam
/**
@@ -124,10 +130,13 @@ public:
*
* @see CORO_END_CONTEXT
*
- * @note We always declare a variable 'DUMMY' to allow the user to specify
- * an 'empty' context.
+ * @note We declare a variable 'DUMMY' to allow the user to specify an 'empty'
+ * context, and so compilers won't complain about ";" following the macro.
*/
-#define CORO_BEGIN_CONTEXT struct CoroContextTag : CoroBaseContext { int DUMMY
+#define CORO_BEGIN_CONTEXT \
+ struct CoroContextTag : CoroBaseContext { \
+ CoroContextTag() : CoroBaseContext(SCUMMVM_CURRENT_FUNCTION) {} \
+ int DUMMY
/**
* End the declaration of a coroutine context.
@@ -152,7 +161,10 @@ public:
* @see CORO_END_CODE
*/
#define CORO_END_CODE \
- if (&coroParam == &nullContext) nullContext = NULL; \
+ if (&coroParam == &nullContext) { \
+ delete nullContext; \
+ nullContext = NULL; \
+ } \
}
/**
@@ -174,11 +186,28 @@ public:
#define CORO_KILL_SELF() \
do { if (&coroParam != &nullContext) { coroParam->_sleep = -1; } return; } while (0)
+
+/**
+ * This macro is to be used in conjunction with CORO_INVOKE_ARGS and
+ * similar macros for calling coroutines-enabled subroutines.
+ */
+#define CORO_SUBCTX coroParam->_subctx
+
/**
* Invoke another coroutine.
*
* What makes this tricky is that the coroutine we called my yield/sleep,
* and we need to deal with this adequately.
+ *
+ * @param subCoro name of the coroutine-enabled function to invoke
+ * @param ARGS list of arguments to pass to subCoro
+ *
+ * @note ARGS must be surrounded by parentheses, and the first argument
+ * in this list must always be CORO_SUBCTX. For example, the
+ * regular function call
+ * myFunc(a, b);
+ * becomes the following:
+ * CORO_INVOKE_ARGS(myFunc, (CORO_SUBCTX, a, b));
*/
#define CORO_INVOKE_ARGS(subCoro, ARGS) \
do {\
diff --git a/engines/tinsel/cursor.cpp b/engines/tinsel/cursor.cpp
index dd5f024815..66dc47df1f 100644
--- a/engines/tinsel/cursor.cpp
+++ b/engines/tinsel/cursor.cpp
@@ -57,6 +57,8 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static OBJECT *McurObj = NULL; // Main cursor object
static OBJECT *AcurObj = NULL; // Auxiliary cursor object
diff --git a/engines/tinsel/debugger.cpp b/engines/tinsel/debugger.cpp
index f422c88e94..ed877587c7 100644
--- a/engines/tinsel/debugger.cpp
+++ b/engines/tinsel/debugger.cpp
@@ -57,7 +57,8 @@ int strToInt(const char *s) {
// Hexadecimal string
uint tmp;
- sscanf(s, "%xh", &tmp);
+ if (!sscanf(s, "%xh", &tmp))
+ tmp = 0;
return (int)tmp;
}
@@ -116,7 +117,7 @@ bool Console::cmd_music(int argc, const char **argv) {
int param = strToInt(argv[1]);
if (param == 0) {
- DebugPrintf("Track number/offset can't be 0!\n", argv[0]);
+ DebugPrintf("Track number/offset can't be 0!\n");
} else if (param > 0) {
// Track provided
PlayMidiSequence(GetTrackOffset(param - 1), false);
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index d6bdad6032..22e8806e7e 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -133,7 +133,14 @@ bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
bool Tinsel::TinselEngine::hasFeature(EngineFeature f) const {
return
#if 0
- // FIXME: tinsel does not exit cleanly yet
+ // FIXME: It is possible to return to the launcher from tinsel.
+ // But then any attempt to re-enter the engine will lead to
+ // a crash or at least seriously broken behavior.
+ //
+ // This is because the Tinsel engine makes use of tons of
+ // global variables (static and non-static) which are never
+ // explicitly re-initialized when the engine is started
+ // for a second time.
(f == kSupportsRTL) ||
#endif
(f == kSupportsLoadingDuringRuntime);
@@ -184,7 +191,7 @@ bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGa
struct SizeMD5 {
int size;
- char md5[32+1];
+ Common::String md5;
};
typedef Common::HashMap<Common::String, SizeMD5, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SizeMD5Map;
typedef Common::HashMap<Common::String, Common::FSNode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
@@ -205,8 +212,13 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &
if (fslist.empty())
return NULL;
+ // TODO: The following code is essentially a slightly modified copy of the
+ // complete code of function detectGame() in engines/advancedDetector.cpp.
+ // That quite some hefty and undesirable code duplication. Its only purpose
+ // seems to be to treat filenames of the form "foo1.ext" as "foo.ext".
+ // It would be nice to avoid this code duplication.
+
// First we compose a hashmap of all files in fslist.
- // Includes nifty stuff like removing trailing dots and ignoring case.
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory()) {
if (!scumm_stricmp(file->getName().c_str(), "dw2")) {
@@ -256,11 +268,9 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &
if (testFile.open(allFiles[fname])) {
tmp.size = (int32)testFile.size();
- if (!md5_file_string(testFile, tmp.md5, detectionParams.md5Bytes))
- tmp.md5[0] = 0;
+ tmp.md5 = computeStreamMD5AsString(testFile, detectionParams.md5Bytes);
} else {
tmp.size = -1;
- tmp.md5[0] = 0;
}
filesSizeMD5[fname] = tmp;
@@ -306,7 +316,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &
break;
}
- if (fileDesc->md5 != NULL && 0 != strcmp(fileDesc->md5, filesSizeMD5[tstr].md5)) {
+ if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) {
fileMissing = true;
break;
}
@@ -330,12 +340,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &
if (curFilesMatched > maxFilesMatched) {
maxFilesMatched = curFilesMatched;
- for (uint j = 0; j < matched.size();) {
- if (matched[j]->flags & ADGF_KEEPMATCH)
- ++j;
- else
- matched.remove_at(j);
- }
+ matched.clear(); // Remove any prior, lower ranked matches.
matched.push_back((const ADGameDescription *)g);
} else if (curFilesMatched == maxFilesMatched) {
matched.push_back((const ADGameDescription *)g);
diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h
index a2a32d2e13..2a523470cc 100644
--- a/engines/tinsel/detection_tables.h
+++ b/engines/tinsel/detection_tables.h
@@ -77,7 +77,26 @@ static const TinselGameDescription gameDescriptions[] = {
GF_CD,
TINSEL_V1,
},
-
+#if 0
+ { // Macintosh CD Demo V1 version, with *.scn files
+ {
+ "dw",
+ "CD Demo",
+ {
+ {"dw.scn", 0, "ae291aa4ed7f7caacbfb711b6ff2c8bd", 1286264},
+ {"english.smp", 0, NULL, -1},
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_BIG_ENDIAN,
+ TINSEL_V1,
+ },
+#endif
{ // Multilingual Floppy V1 with *.gra files.
// Note: It contains no english subtitles.
{
@@ -102,6 +121,75 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
+ {
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ {
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ {
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
{ // Floppy V1 version, with *.gra files
{
"dw",
@@ -375,7 +463,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_BIG_ENDIAN,
TINSEL_V1,
},
@@ -419,6 +507,26 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
+ { // English DW2 demo
+ {
+ "dw2",
+ "Demo",
+ {
+ {"dw2.scn", 0, "853ab998f5136b69bc586991175d6eeb", 4231121},
+ {"english.smp", 0, "b5660a0e031cb4710bcb0ef5629ea61d", 28562357},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES | GF_DEMO,
+ TINSEL_V2,
+ },
+
{ // European/Australian Discworld 2 release
{
"dw2",
diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp
index ed59b5669b..73cad7a68f 100644
--- a/engines/tinsel/dialogs.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -1587,7 +1587,7 @@ static bool InvKeyIn(const Common::KeyState &kbd) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL3]);
iconArray[HL3] = NULL;
}
- iconArray[HL3] = ObjectTextOut(nullContext,
+ iconArray[HL3] = ObjectTextOut(
GetPlayfieldList(FIELD_STATUS), sedit, 0,
InvD[ino].inventoryX + cd.box[cd.selBox].xpos + 2,
InvD[ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
@@ -1595,7 +1595,7 @@ static bool InvKeyIn(const Common::KeyState &kbd) {
if (MultiRightmost(iconArray[HL3]) > MAX_NAME_RIGHT) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL3]);
UpdateString(Common::KeyState(Common::KEYCODE_BACKSPACE));
- iconArray[HL3] = ObjectTextOut(nullContext,
+ iconArray[HL3] = ObjectTextOut(
GetPlayfieldList(FIELD_STATUS), sedit, 0,
InvD[ino].inventoryX + cd.box[cd.selBox].xpos + 2,
InvD[ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
@@ -1669,7 +1669,7 @@ static void Select(int i, bool force) {
}
#endif
- iconArray[HL3] = ObjectTextOut(nullContext,
+ iconArray[HL3] = ObjectTextOut(
GetPlayfieldList(FIELD_STATUS), sedit, 0,
InvD[ino].inventoryX + cd.box[i].xpos + 2,
#ifdef JAPAN
@@ -2245,7 +2245,7 @@ static int WhichMenuBox(int curX, int curY, bool bSlides) {
* InvBoxes
*/
static void InvBoxes(bool InBody, int curX, int curY) {
- static int rotateIndex = -1;
+ static int rotateIndex = -1; // FIXME: Avoid non-const global vars
int index; // Box pointed to on this call
const FILM *pfilm;
@@ -2634,17 +2634,16 @@ static void AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV,
return;
// Create text object using title string
- CoroContext dummyCoro;
if (textFrom == FROM_HANDLE) {
LoadStringRes(InvD[ino].hInvTitle, TextBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(dummyCoro, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + width/2, InvD[ino].inventoryY + M_TOFF,
GetTagFontHandle(), TXT_CENTRE);
assert(*title); // Inventory title string produced NULL text
MultiSetZPosition(*title, Z_INV_HTEXT);
} else if (textFrom == FROM_STRING && cd.ixHeading != NO_HEADING) {
LoadStringRes(configStrings[cd.ixHeading], TextBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(dummyCoro, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + width/2, InvD[ino].inventoryY + M_TOFF,
GetTagFontHandle(), TXT_CENTRE);
assert(*title); // Inventory title string produced NULL text
@@ -2668,7 +2667,7 @@ static void AddTitle(POBJECT *title, int extraH) {
// Create text object using title string
if (InvD[ino].hInvTitle != (SCNHANDLE)NO_HEADING) {
LoadStringRes(InvD[ino].hInvTitle, TextBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + (width/2)+NM_BG_POS_X, InvD[ino].inventoryY + NM_TOFF,
GetTagFontHandle(), TXT_CENTRE, 0);
assert(*title);
@@ -2749,14 +2748,14 @@ static void AddBox(int *pi, const int i) {
(!TinselV2 && (cd.box[i].ixText == USE_POINTER))) {
if (cd.box[i].boxText != NULL) {
if (cd.box[i].boxType == RGROUP) {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
#ifdef JAPAN
x + 2, y+2, GetTagFontHandle(), 0);
#else
x + 2, y + TYOFF, GetTagFontHandle(), 0);
#endif
} else {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
#ifdef JAPAN
// Note: it never seems to go here!
x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTRE);
@@ -2782,10 +2781,10 @@ static void AddBox(int *pi, const int i) {
}
if (TinselV2 && (cd.box[i].boxType == RGROUP))
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, x + 2, y + TYOFF, GetTagFontHandle(), 0, 0);
else
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0,
#ifdef JAPAN
x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTRE);
@@ -2842,7 +2841,7 @@ static void AddBox(int *pi, const int i) {
assert(cd.box[i].ixText != USE_POINTER);
LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
}
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
@@ -2869,11 +2868,11 @@ static void AddBox(int *pi, const int i) {
}
if (cd.box[i].boxType == TOGGLE2) {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
GetTagFontHandle(), TXT_CENTRE, 0);
} else {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF,
GetTagFontHandle(), TXT_RIGHT, 0);
}
@@ -2908,7 +2907,7 @@ static void AddBox(int *pi, const int i) {
assert(cd.box[i].ixText != USE_POINTER);
LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
}
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x+MDTEXT_XOFF, y+MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
@@ -2933,7 +2932,7 @@ static void AddBox(int *pi, const int i) {
// Stick in the text
assert(cd.box[i].textMethod == TM_INDEX);
LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
GetTagFontHandle(), TXT_CENTRE, 0);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
@@ -2945,7 +2944,7 @@ static void AddBox(int *pi, const int i) {
break;
LoadStringRes(LanguageDesc(displayedLanguage), TextBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
x + cd.box[i].w / 2, y + ROT_YOFF, GetTagFontHandle(), TXT_CENTRE, 0);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
@@ -3410,7 +3409,7 @@ enum InvCursorFN {IC_AREA, IC_DROP};
*/
static void InvCursor(InvCursorFN fn, int CurX, int CurY) {
static enum { IC_NORMAL, IC_DR, IC_UR, IC_TB, IC_LR,
- IC_INV, IC_UP, IC_DN } ICursor = IC_NORMAL; // FIXME: local static var
+ IC_INV, IC_UP, IC_DN } ICursor = IC_NORMAL; // FIXME: Avoid non-const global vars
int area; // The part of the window the cursor is over
bool restoreMain = false;
@@ -4279,7 +4278,7 @@ static int NearestSlideY(int fity) {
* y-movement during such a drag.
*/
static void SlideSlider(int y, SSFN fn) {
- static int newY = 0, lasti = 0; // FIXME: local static var
+ static int newY = 0, lasti = 0; // FIXME: Avoid non-const global vars
int gotoY, ati;
// Only do this if there's a slider
@@ -4333,7 +4332,7 @@ static void SlideSlider(int y, SSFN fn) {
* y-movement during such a drag.
*/
static void SlideCSlider(int y, SSFN fn) {
- static int newY = 0; // FIXME: local static var
+ static int newY = 0; // FIXME: Avoid non-const global vars
int gotoY;
int fc;
@@ -4400,7 +4399,7 @@ static void SlideCSlider(int y, SSFN fn) {
* and upon x-movement during such a drag.
*/
static void SlideMSlider(int x, SSFN fn) {
- static int newX = 0; // FIXME: local static var
+ static int newX = 0; // FIXME: Avoid non-const global vars
int gotoX;
int index, i;
@@ -5552,6 +5551,21 @@ extern void RegisterIcons(void *cptr, int num) {
memmove(destP, srcP, 12);
destP->attribute = 0;
}
+ } else if (TinselV1Mac) {
+ // Macintosh version has BE encoded resources, so the values need to be byte swapped
+ MEM_NODE *node = MemoryAllocFixed(numObjects * sizeof(INV_OBJECT));
+ assert(node);
+ invObjects = (INV_OBJECT *)MemoryDeref(node);
+ assert(invObjects);
+ INV_OBJECT *srcP = (INV_OBJECT *)cptr;
+ INV_OBJECT *destP = (INV_OBJECT *)invObjects;
+
+ for (int i = 0; i < num; ++i, ++destP, ++srcP) {
+ destP->id = FROM_BE_32(srcP->id);
+ destP->hIconFilm = FROM_BE_32(srcP->hIconFilm);
+ destP->hScript = FROM_BE_32(srcP->hScript);
+ destP->attribute = FROM_BE_32(srcP->attribute);
+ }
} else if (TinselV2) {
if (invFilms == NULL) {
// First time - allocate memory
diff --git a/engines/tinsel/drives.cpp b/engines/tinsel/drives.cpp
index efc7a2046d..6e30caf006 100644
--- a/engines/tinsel/drives.cpp
+++ b/engines/tinsel/drives.cpp
@@ -24,6 +24,8 @@
* CD/drive handling functions
*/
+#include "common/config-manager.h"
+#include "common/substream.h"
#include "gui/message.h"
#include "tinsel/drives.h"
#include "tinsel/scene.h"
@@ -33,12 +35,17 @@
namespace Tinsel {
+// FIXME: Avoid non-const global vars
+
char currentCD = '1';
static uint32 cdFlags[] = { fCd1, fCd2, fCd3, fCd4, fCd5, fCd6, fCd7, fCd8 };
static bool bChangingCD = false;
static char nextCD = '\0';
+static uint32 lastTime = 0;
+extern LANGUAGE sampleLanguage;
+
void CdCD(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
@@ -94,9 +101,6 @@ int GetCD(int flags) {
return cd;
}
-static uint32 lastTime = 0;
-extern LANGUAGE sampleLanguage;
-
void DoCdChange() {
if (bChangingCD && (g_system->getMillis() > (lastTime + 1000))) {
lastTime = g_system->getMillis();
@@ -149,13 +153,24 @@ bool GotoCD() {
bool TinselFile::_warningShown = false;
-bool TinselFile::open(const Common::String &filename) {
- if (Common::File::open(filename)) {
- // If it's the sample file, strip off the CD number from the filename
+TinselFile::TinselFile() : ReadStreamEndian((_vm->getFeatures() & GF_BIG_ENDIAN) != 0) {
+ _stream = NULL;
+}
+TinselFile::~TinselFile() {
+ delete _stream;
+}
+
+bool TinselFile::openInternal(const Common::String &filename) {
+ _stream = SearchMan.createReadStreamForMember(filename);
+ if (!_stream)
+ _stream = SearchMan.createReadStreamForMember(filename + ".");
+ return _stream != 0;
+}
+bool TinselFile::open(const Common::String &filename) {
+ if (openInternal(filename))
return true;
- }
if (!TinselV2)
return false;
@@ -173,7 +188,50 @@ bool TinselFile::open(const Common::String &filename) {
strncpy(newFilename, fname, p - fname);
strcpy(newFilename + (p - fname), p + 1);
- return Common::File::open(newFilename);
+ return openInternal(newFilename);
+}
+
+void TinselFile::close() {
+ delete _stream;
+ _stream = NULL;
}
+int32 TinselFile::pos() const {
+ assert(_stream);
+ return _stream->pos();
+}
+
+int32 TinselFile::size() const {
+ assert(_stream);
+ return _stream->size();
+}
+
+bool TinselFile::seek(int32 offset, int whence) {
+ assert(_stream);
+ return _stream->seek(offset, whence);
+}
+
+bool TinselFile::eos() const {
+ assert(_stream);
+ return _stream->eos();
+}
+
+bool TinselFile::err() const {
+ assert(_stream);
+ return _stream->err();
+}
+
+void TinselFile::clearErr() {
+ assert(_stream);
+ _stream->clearErr();
+}
+
+uint32 TinselFile::read(void *dataPtr, uint32 dataSize) {
+ assert(_stream);
+ return _stream->read(dataPtr, dataSize);
+}
+
+
+
+
} // End of namespace Tinsel
diff --git a/engines/tinsel/drives.h b/engines/tinsel/drives.h
index 81111265fa..709c0d2392 100644
--- a/engines/tinsel/drives.h
+++ b/engines/tinsel/drives.h
@@ -27,7 +27,7 @@
#ifndef TINSEL_DRIVES_H
#define TINSEL_DRIVES_H
-#include "common/file.h"
+#include "common/stream.h"
#include "tinsel/dw.h"
#include "tinsel/coroutine.h"
@@ -59,12 +59,27 @@ void SetNextCD(int cdNumber);
bool GotoCD();
-class TinselFile: public Common::File {
+class TinselFile : public Common::SeekableReadStream, public Common::ReadStreamEndian {
private:
static bool _warningShown;
+ Common::SeekableReadStream *_stream;
+ bool openInternal(const Common::String &filename);
public:
- virtual bool open(const Common::String &filename);
+ TinselFile();
+ ~TinselFile();
+ bool open(const Common::String &filename);
+ void close();
char getCdNumber();
+
+ bool err() const;
+ void clearErr();
+
+ bool eos() const;
+ uint32 read(void *dataPtr, uint32 dataSize);
+
+ int32 pos() const;
+ int32 size() const;
+ bool seek(int32 offset, int whence = SEEK_SET);
};
diff --git a/engines/tinsel/events.cpp b/engines/tinsel/events.cpp
index 9c01f15b2a..4e61a6ef29 100644
--- a/engines/tinsel/events.cpp
+++ b/engines/tinsel/events.cpp
@@ -61,6 +61,8 @@ extern bool bEnableMenu;
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static uint32 lastUserEvent = 0; // Time it hapenned
static int leftEvents = 0; // Single or double, left or right. Or escape key.
static int escEvents = 1; // Escape key
@@ -393,7 +395,7 @@ void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
"PLR_JUMP", "PLR_NOEVENT"};
debugC(DEBUG_BASIC, kTinselDebugActions, "%s - (%d,%d)",
actionList[pEvent], coOrds.x, coOrds.y);
- static uint32 lastRealAction = 0;
+ static uint32 lastRealAction = 0; // FIXME: Avoid non-const global vars
// This stuff to allow F1 key during startup.
if (bEnableMenu && pEvent == PLR_MENU)
diff --git a/engines/tinsel/faders.cpp b/engines/tinsel/faders.cpp
index de04e4a33e..a82285b12f 100644
--- a/engines/tinsel/faders.cpp
+++ b/engines/tinsel/faders.cpp
@@ -236,8 +236,8 @@ void FadeInFast(SCNHANDLE noFadeTable[]) {
void PokeInTagColour() {
if (SysVar(SV_TAGCOLOUR)) {
- static COLORREF c = GetActorRGB(-1);
- UpdateDACqueue(SysVar(SV_TAGCOLOUR), 1, &c);
+ const COLORREF c = GetActorRGB(-1);
+ UpdateDACqueue(SysVar(SV_TAGCOLOUR), c);
}
}
diff --git a/engines/tinsel/font.cpp b/engines/tinsel/font.cpp
index ae7da65473..4c76d12400 100644
--- a/engines/tinsel/font.cpp
+++ b/engines/tinsel/font.cpp
@@ -35,6 +35,8 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static char tBuffer[TBUFSZ];
static SCNHANDLE hTagFont = 0, hTalkFont = 0;
@@ -117,9 +119,9 @@ void FettleFontPal(SCNHANDLE fontPal) {
pImg->hImgPal = 0;
if (TinselV2 && SysVar(SV_TAGCOLOUR)) {
- static COLORREF c = GetActorRGB(-1);
+ const COLORREF c = GetActorRGB(-1);
SetTagColorRef(c);
- UpdateDACqueue(SysVar(SV_TAGCOLOUR), 1, &c);
+ UpdateDACqueue(SysVar(SV_TAGCOLOUR), c);
}
}
diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp
index 48270d94e3..aefc6e6144 100644
--- a/engines/tinsel/graphics.cpp
+++ b/engines/tinsel/graphics.cpp
@@ -66,8 +66,8 @@ uint8* psxPJCRLEUnwinder(uint16 imageWidth, uint16 imageHeight, uint8 *srcIdx) {
return NULL;
// Calculate needed index numbers, align width and height not next multiple of four
- imageWidth = imageWidth % 4 ? ((imageWidth / 4) + 1) * 4 : imageWidth;
- imageHeight = imageHeight % 4 ? ((imageHeight / 4) + 1) * 4 : imageHeight;
+ imageWidth = (imageWidth % 4) ? ((imageWidth / 4) + 1) * 4 : imageWidth;
+ imageHeight = (imageHeight % 4) ? ((imageHeight / 4) + 1) * 4 : imageHeight;
destinationBuffer = (uint8*)malloc((imageWidth * imageHeight) / 8);
dstIdx = destinationBuffer;
remainingBlocks = (imageWidth * imageHeight) / 16;
@@ -297,7 +297,7 @@ static void PsxDrawTiles(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool apply
} else {
for (int xp = boxBounds.left; xp <= boxBounds.right; ++xp) {
// Extract pixel value from byte
- byte pixValue = (*(p + (xp / 2)) & (xp % 2 ? 0xf0 : 0x0f)) >> (xp % 2 ? 4 : 0);
+ byte pixValue = (*(p + (xp / 2)) & ((xp % 2) ? 0xf0 : 0x0f)) >> ((xp % 2) ? 4 : 0);
if (pixValue || !transparency)
*(tempDest + SCREEN_WIDTH * (yp - boxBounds.top) + (xp - boxBounds.left)) = psxMapperTable[pixValue];
}
diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp
index fdc4484a7c..2307d95a61 100644
--- a/engines/tinsel/handle.cpp
+++ b/engines/tinsel/handle.cpp
@@ -70,6 +70,8 @@ enum {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
// handle table gets loaded from index file at runtime
static MEMHANDLE *handleTable = 0;
@@ -100,11 +102,13 @@ void SetupHandleTable() {
int len;
uint i;
MEMHANDLE *pH;
- Common::File f;
+ TinselFile f;
if (f.open(TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)) {
// get size of index file
len = f.size();
+ if (TinselV1Mac)
+ len -= 2; // Macintosh version has two redundant ending bytes
if (len > 0) {
if ((len % RECORD_SIZE) != 0) {
@@ -124,13 +128,13 @@ void SetupHandleTable() {
// load data
for (i = 0; i < numHandles; i++) {
f.read(handleTable[i].szName, 12);
- handleTable[i].filesize = f.readUint32LE();
+ handleTable[i].filesize = f.readUint32();
// The pointer should always be NULL. We don't
// need to read that from the file.
handleTable[i]._node = NULL;
f.seek(4, SEEK_CUR);
// For Discworld 2, read in the flags2 field
- handleTable[i].flags2 = t2Flag ? f.readUint32LE() : 0;
+ handleTable[i].flags2 = t2Flag ? f.readUint32() : 0;
}
if (f.eos() || f.err()) {
diff --git a/engines/tinsel/heapmem.cpp b/engines/tinsel/heapmem.cpp
index fa05c3717b..e8c4ed23ee 100644
--- a/engines/tinsel/heapmem.cpp
+++ b/engines/tinsel/heapmem.cpp
@@ -57,6 +57,9 @@ struct MEM_NODE {
// If the memory is not enough, the engine throws an "Out of memory" error in handle.cpp inside LockMem()
static const uint32 MemoryPoolSize[3] = {5 * 1024 * 1024, 5 * 1024 * 1024, 10 * 1024 * 1024};
+// FIXME: Avoid non-const global vars
+
+
// list of all memory nodes
MEM_NODE mnodeList[NUM_MNODES];
@@ -94,7 +97,7 @@ static void MemoryStats() {
}
}
- printf("%d nodes used, %d alloced, %d locked; %d bytes locked, %d used\n",
+ debug("%d nodes used, %d alloced, %d locked; %d bytes locked, %d used",
usedNodes, allocedNodes, lockedNodes, lockedSize, totalSize);
}
#endif
diff --git a/engines/tinsel/mareels.cpp b/engines/tinsel/mareels.cpp
index 33e96ece58..cf28749e76 100644
--- a/engines/tinsel/mareels.cpp
+++ b/engines/tinsel/mareels.cpp
@@ -48,6 +48,8 @@ struct SCIdataStruct {
SCNHANDLE reels[4];
};
+// FIXME: Avoid non-const global vars
+
static SCIdataStruct SCIdata[MAX_SCRENTRIES];
static int scrEntries = 0;
diff --git a/engines/tinsel/module.mk b/engines/tinsel/module.mk
index 6c818bcb0f..2778cec3df 100644
--- a/engines/tinsel/module.mk
+++ b/engines/tinsel/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
bmv.o \
cliprect.o \
config.o \
+ coroutine.o \
cursor.o \
debugger.o \
detection.o \
diff --git a/engines/tinsel/move.cpp b/engines/tinsel/move.cpp
index f854dba2fd..5b9e650689 100644
--- a/engines/tinsel/move.cpp
+++ b/engines/tinsel/move.cpp
@@ -77,6 +77,8 @@ HPOLYGON InitExtraBlock(PMOVER ca, PMOVER ta);
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
#if SLOW_RINCE_DOWN
static int Interlude = 0; // For slowing down walking, for testing
static int BogusVar = 0; // For slowing down walking, for testing
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index 0901cd08b8..ad09631260 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -31,8 +31,12 @@
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "sound/decoders/adpcm.h"
+
+#include "backends/audiocd/audiocd.h"
+
#include "common/config-manager.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "tinsel/config.h"
#include "tinsel/sound.h"
@@ -59,6 +63,8 @@ struct SOUND_BUFFER {
uint32 size; // size of the buffer
};
+// FIXME: Avoid non-const global vars
+
// get set when music driver is installed
//static MDI_DRIVER *mDriver;
//static HSEQUENCE mSeqHandle;
@@ -165,7 +171,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
}
// the index and length of the last tune loaded
- static uint32 dwLastMidiIndex = 0;
+ static uint32 dwLastMidiIndex = 0; // FIXME: Avoid non-const global vars
//static uint32 dwLastSeqLen;
uint32 dwSeqLen = 0; // length of the sequence
@@ -305,7 +311,7 @@ int GetMidiVolume() {
void SetMidiVolume(int vol) {
assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume);
- static int priorVolMusic = 0;
+ static int priorVolMusic = 0; // FIXME: Avoid non-const global vars
if (vol == 0 && priorVolMusic == 0) {
// Nothing to do
@@ -381,7 +387,7 @@ void OpenMidiFiles() {
assert(curTrack < ARRAYSIZE(midiOffsets));
midiOffsets[curTrack] = curOffset + (4 * curTrack);
- //printf("%d: %d\n", curTrack, midiOffsets[curTrack]);
+ //debug("%d: %d", curTrack, midiOffsets[curTrack]);
songLength = midiStream.readUint32LE();
curOffset += songLength;
@@ -830,7 +836,7 @@ bool PCMMusicPlayer::getNextChunk() {
uint32 sampleOffset, sampleLength, sampleCLength;
Common::File file;
byte *buffer;
- Common::MemoryReadStream *sampleStream;
+ Common::SeekableReadStream *sampleStream;
switch (_state) {
case S_NEW:
diff --git a/engines/tinsel/object.cpp b/engines/tinsel/object.cpp
index 7a93a0b30a..bf31cdfa25 100644
--- a/engines/tinsel/object.cpp
+++ b/engines/tinsel/object.cpp
@@ -36,6 +36,8 @@
namespace Tinsel {
+// FIXME: Avoid non-const global vars
+
// list of all objects
static OBJECT *objectList = 0;
@@ -94,7 +96,7 @@ void KillAllObjects() {
*/
void ObjectStats() {
- printf("%i objects of %i used.\n", maxObj, NUM_OBJECTS);
+ debug("%i objects of %i used", maxObj, NUM_OBJECTS);
}
#endif
diff --git a/engines/tinsel/palette.cpp b/engines/tinsel/palette.cpp
index ec518d9e68..affc855744 100644
--- a/engines/tinsel/palette.cpp
+++ b/engines/tinsel/palette.cpp
@@ -42,6 +42,7 @@ struct VIDEO_DAC_Q {
union {
SCNHANDLE hRGBarray; ///< handle of palette or
COLORREF *pRGBarray; ///< list of palette colours
+ COLORREF singleRGB;
} pal;
bool bHandle; ///< when set - use handle of palette
int destDACindex; ///< start index of palette in video DAC
@@ -51,8 +52,7 @@ struct VIDEO_DAC_Q {
//----------------- LOCAL GLOBAL DATA --------------------
-/** background colour */
-static COLORREF bgndColour = BLACK;
+// FIXME: Avoid non-const global vars
/** palette allocator data */
static PALQ palAllocData[NUM_PALETTES];
@@ -136,8 +136,8 @@ void PalettesToVideoDAC() {
// while Q is not empty
while (pDAChead != pDACtail) {
- PALETTE *pPalette; // pointer to hardware palette
- COLORREF *pColours; // pointer to list of RGB triples
+ const PALETTE *pPalette; // pointer to hardware palette
+ const COLORREF *pColours; // pointer to list of RGB triples
#ifdef DEBUG
// make sure palette does not overlap
@@ -152,17 +152,20 @@ void PalettesToVideoDAC() {
// we are using a palette handle
// get hardware palette pointer
- pPalette = (PALETTE *)LockMem(pDACtail->pal.hRGBarray);
+ pPalette = (const PALETTE *)LockMem(pDACtail->pal.hRGBarray);
// get RGB pointer
pColours = pPalette->palRGB;
+ } else if (pDACtail->numColours == 1) {
+ // we are using a single color palette
+ pColours = &pDACtail->pal.singleRGB;
} else {
// we are using a palette pointer
pColours = pDACtail->pal.pRGBarray;
}
// update the system palette
- g_system->setPalette((byte *)pColours, pDACtail->destDACindex, pDACtail->numColours);
+ g_system->setPalette((const byte *)pColours, pDACtail->destDACindex, pDACtail->numColours);
// update tail pointer
pDACtail++;
@@ -198,8 +201,8 @@ void ResetPalAllocator() {
* Shows the maximum number of palettes used at once.
*/
void PaletteStats() {
- printf("%i palettes of %i used.\n", maxPals, NUM_PALETTES);
- printf("%i DAC queue entries of %i used.\n", maxDACQ, VDACQLENGTH);
+ debug("%i palettes of %i used", maxPals, NUM_PALETTES);
+ debug("%i DAC queue entries of %i used", maxDACQ, VDACQLENGTH);
}
#endif
@@ -230,7 +233,7 @@ void UpdateDACqueueHandle(int posInDAC, int numColours, SCNHANDLE hPalette) {
/**
* Places a palette in the video DAC queue.
* @param posInDAC Position in video DAC
- * @param numColours, Number of colours in palette
+ * @param numColours Number of colours in palette
* @param pColours List of RGB triples
*/
void UpdateDACqueue(int posInDAC, int numColours, COLORREF *pColours) {
@@ -239,7 +242,34 @@ void UpdateDACqueue(int posInDAC, int numColours, COLORREF *pColours) {
pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
pDAChead->numColours = numColours; // set number of colours
- pDAChead->pal.pRGBarray = pColours; // set addr of palette
+ if (numColours == 1)
+ pDAChead->pal.singleRGB = *pColours; // set single color of which the "palette" consists
+ else
+ pDAChead->pal.pRGBarray = pColours; // set addr of palette
+ pDAChead->bHandle = false; // we are not using a palette handle
+
+ // update head pointer
+ ++pDAChead;
+
+#ifdef DEBUG
+ if ((pDAChead-vidDACdata) > maxDACQ)
+ maxDACQ = pDAChead-vidDACdata;
+#endif
+}
+
+
+/**
+ * Places a "palette" consisting of a single color in the video DAC queue.
+ * @param posInDAC Position in video DAC
+ * @param color Single RGB triple
+ */
+void UpdateDACqueue(int posInDAC, COLORREF color) {
+ // check Q overflow
+ assert(pDAChead < vidDACdata + NUM_PALETTES);
+
+ pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
+ pDAChead->numColours = 1; // set number of colours
+ pDAChead->pal.singleRGB = color; // set single color of which the "palette" consists
pDAChead->bHandle = false; // we are not using a palette handle
// update head pointer
@@ -468,11 +498,8 @@ PALQ *GetNextPalette(PALQ *pStrtPal) {
* @param colour Colour to set the background to
*/
void SetBgndColour(COLORREF colour) {
- // update background colour struct
- bgndColour = colour;
-
- // Q the change to the video DAC
- UpdateDACqueue(BGND_DAC_INDEX, 1, &bgndColour);
+ // update background colour struct by queuing the change to the video DAC
+ UpdateDACqueue(BGND_DAC_INDEX, colour);
}
/**
@@ -480,7 +507,7 @@ void SetBgndColour(COLORREF colour) {
* @param pPalQ Palette queue position
* @param bFading Whether it is fading
*/
-void FadingPalette(PPALQ pPalQ, bool bFading) {
+void FadingPalette(PALQ *pPalQ, bool bFading) {
// validate palette Q pointer
assert(pPalQ >= palAllocData && pPalQ <= palAllocData + NUM_PALETTES - 1);
@@ -495,7 +522,7 @@ void FadingPalette(PPALQ pPalQ, bool bFading) {
* palettes are fading.
*/
void NoFadingPalettes() {
- PPALQ pPalQ;
+ PALQ *pPalQ;
for (pPalQ = palAllocData; pPalQ <= palAllocData + NUM_PALETTES - 1; pPalQ++) {
pPalQ->bFading = false;
@@ -619,10 +646,7 @@ int TranslucentColour() {
}
int HighlightColour() {
- static COLORREF cRef;
-
- cRef = (COLORREF)SysVar(SYS_HighlightRGB);
- UpdateDACqueue(talkIndex, 1, &cRef);
+ UpdateDACqueue(talkIndex, (COLORREF)SysVar(SYS_HighlightRGB));
return talkIndex;
}
diff --git a/engines/tinsel/palette.h b/engines/tinsel/palette.h
index e4cee3e301..9743ee53aa 100644
--- a/engines/tinsel/palette.h
+++ b/engines/tinsel/palette.h
@@ -90,7 +90,6 @@ struct PALQ {
bool bFading; // Whether or not fading
COLORREF palRGB[MAX_COLOURS]; // actual palette colours
};
-typedef PALQ *PPALQ;
#define PALETTE_MOVED 0x8000 // when this bit is set in the "posInDAC"
// field - the palette entry has moved
@@ -123,6 +122,8 @@ void UpdateDACqueue( // places a palette in the video DAC queue
int numColours, // number of colours in palette
COLORREF *pColours); // list of RGB tripples
+void UpdateDACqueue(int posInDAC, COLORREF color);
+
PALQ *AllocPalette( // allocate a new palette
SCNHANDLE hNewPal); // palette to allocate
@@ -144,7 +145,7 @@ COLORREF GetBgndColour(); // returns current background colour
void SetBgndColour( // sets current background colour
COLORREF colour); // colour to set the background to
-void FadingPalette(PPALQ pPalQ, bool bFading);
+void FadingPalette(PALQ *pPalQ, bool bFading);
void CreateTranslucentPalette(SCNHANDLE BackPal);
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index 98fb078459..ccd86d7ed7 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -106,6 +106,8 @@ bool bNoPause = false;
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static int32 *pGlobals = 0; // global vars
static int numGlobals = 0; // How many global variables to save/restore
diff --git a/engines/tinsel/pdisplay.cpp b/engines/tinsel/pdisplay.cpp
index e05a6f6a9a..38748b703b 100644
--- a/engines/tinsel/pdisplay.cpp
+++ b/engines/tinsel/pdisplay.cpp
@@ -72,6 +72,8 @@ enum HotSpotTag {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static bool DispPath = false;
static bool bShowString = false;
@@ -155,7 +157,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// New text objects
sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
- _ctx->cpText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
if (DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
@@ -167,7 +169,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
PolyCornerX(hp, 1), PolyCornerY(hp, 1),
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
- _ctx->cpathText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, 4, POSY+ 10, GetTagFontHandle(), 0);
}
@@ -213,7 +215,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// create new text object list
sprintf(PositionString, "%d %d", aniX, aniY);
- _ctx->rpText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous position
@@ -232,7 +234,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
}
sprintf(PositionString, "String: %d", newestString);
- _ctx->spText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTRE);
// update previous value
@@ -375,6 +377,7 @@ static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
* the screen.
*/
static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
+ // FIXME: Avoid non-const global vars
static int tagX = 0, tagY = 0; // Values when tag was displayed
int newX, newY; // new values, to keep tag in place
int ano;
@@ -407,7 +410,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
// May have buggered cursor
EndCursorFollowed();
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), tagBuffer,
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tagBuffer,
0, tagX, tagY, GetTagFontHandle(), TXT_CENTRE, 0);
assert(*ppText);
MultiSetZPosition(*ppText, Z_TAG_TEXT);
@@ -452,7 +455,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
@@ -486,6 +489,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
* code contains a printtag() call, its tagState flag gets set to TAG_ON.
*/
static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
+ // FIXME: Avoid non-const global vars
static int Loffset = 0, Toffset = 0; // Values when tag was displayed
static int curX = 0, curY = 0;
int nLoff, nToff; // new values, to keep tag in place
@@ -555,7 +559,7 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
// May have buggered cursor
EndCursorFollowed();
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTRE, 0);
} else if (TinselV2) {
@@ -565,11 +569,11 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
StartCursorFollowed();
GetCursorXYNoWait(&curX, &curY, false);
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, curX, curY, GetTagFontHandle(), TXT_CENTRE, 0);
} else {
// Handle displaying the tag text on-screen
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Polygon tag string produced NULL text
diff --git a/engines/tinsel/play.cpp b/engines/tinsel/play.cpp
index 103b8f42ad..145634b9dd 100644
--- a/engines/tinsel/play.cpp
+++ b/engines/tinsel/play.cpp
@@ -63,6 +63,8 @@ struct PPINIT {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static SOUNDREELS soundReels[MAX_SOUNDREELS];
static int soundReelNumbers[MAX_SOUNDREELS];
@@ -432,6 +434,7 @@ static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) {
int tmpX, tmpY;
CORO_END_CONTEXT(_ctx);
+ // FIXME: Avoid non-const global vars
static int firstColZ = 0; // Z-position of column zero
static int32 fColZfactor = 0; // Z-factor of column zero's actor
@@ -818,6 +821,7 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA
SoundReelWaitCheck();
} else {
+ // FIXME: Avoid non-const global vars
static int baseZposn; // Z-position of column zero
static uint32 baseZfact; // Z-factor of column zero's actor
diff --git a/engines/tinsel/polygons.cpp b/engines/tinsel/polygons.cpp
index 7fc7fcd218..1620881b01 100644
--- a/engines/tinsel/polygons.cpp
+++ b/engines/tinsel/polygons.cpp
@@ -289,6 +289,8 @@ void Poly::nextPoly() {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static int MaxPolys = MAX_POLY;
static POLYGON *Polys[MAX_POLY+1];
diff --git a/engines/tinsel/rince.cpp b/engines/tinsel/rince.cpp
index cfffe88587..6ea1dd7464 100644
--- a/engines/tinsel/rince.cpp
+++ b/engines/tinsel/rince.cpp
@@ -53,7 +53,7 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
-static MOVER Movers[MAX_MOVERS];
+static MOVER Movers[MAX_MOVERS]; // FIXME: Avoid non-const global vars
//----------------- FUNCTIONS ----------------------------
@@ -361,8 +361,8 @@ static void InitMover(PMOVER pMover) {
pMover->Tline = 0;
- if (pMover->direction != FORWARD || pMover->direction != AWAY
- || pMover->direction != LEFTREEL || pMover->direction != RIGHTREEL)
+ if (pMover->direction != FORWARD && pMover->direction != AWAY
+ && pMover->direction != LEFTREEL && pMover->direction != RIGHTREEL)
pMover->direction = FORWARD;
if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES)
@@ -853,7 +853,7 @@ void T2MoverProcess(CORO_PARAM, const void *param) {
*/
void MoverProcessCreate(int X, int Y, int id, PMOVER pMover) {
if (TinselV2) {
- static MAINIT iStruct;
+ MAINIT iStruct;
iStruct.X = X;
iStruct.Y = Y;
iStruct.pMover = pMover;
diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp
index b010ad1fcb..c965666e84 100644
--- a/engines/tinsel/saveload.cpp
+++ b/engines/tinsel/saveload.cpp
@@ -106,8 +106,22 @@ enum {
#define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
+enum {
+ // FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the
+ // name field in savedFiles. Raising it to 256 as a preliminary fix.
+ FNAMELEN = 256 // 8.3
+};
+
+struct SFILES {
+ char name[FNAMELEN];
+ char desc[SG_DESC_LEN + 2];
+ TimeDate dateTime;
+};
+
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static int numSfiles = 0;
static SFILES savedFiles[MAX_SAVED_FILES];
@@ -275,7 +289,7 @@ static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) {
* Generates a new, unique, filename.
*/
static char *NewName() {
- static char result[FNAMELEN];
+ static char result[FNAMELEN]; // FIXME: Avoid non-const global vars
int i;
int ano = 1; // Allocated number
@@ -318,9 +332,9 @@ static int cmpTimeDate(const TimeDate &a, const TimeDate &b) {
}
/**
- * Interrogate the current DOS directory for saved game files.
+ * Compute a list of all available saved game files.
* Store the file details, ordered by time, in savedFiles[] and return
- * the number of files found).
+ * the number of files found.
*/
int getList(Common::SaveFileManager *saveFileMan, const Common::String &target) {
// No change since last call?
diff --git a/engines/tinsel/savescn.cpp b/engines/tinsel/savescn.cpp
index 2b5c815d3c..a3fe393b79 100644
--- a/engines/tinsel/savescn.cpp
+++ b/engines/tinsel/savescn.cpp
@@ -82,6 +82,8 @@ extern SRSTATE SRstate;
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static bool ASceneIsSaved = false;
static int savedSceneCount = 0;
diff --git a/engines/tinsel/savescn.h b/engines/tinsel/savescn.h
index f0927baae3..271cdd5eb1 100644
--- a/engines/tinsel/savescn.h
+++ b/engines/tinsel/savescn.h
@@ -40,17 +40,7 @@ namespace Tinsel {
enum {
SG_DESC_LEN = 40, // Max. saved game description length
- MAX_SAVED_FILES = 100,
-
- // FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the
- // name field in savedFiles. Raising it to 256 as a preliminary fix.
- FNAMELEN = 256 // 8.3
-};
-
-struct SFILES {
- char name[FNAMELEN];
- char desc[SG_DESC_LEN + 2];
- TimeDate dateTime;
+ MAX_SAVED_FILES = 100
};
struct SAVED_DATA {
diff --git a/engines/tinsel/scene.cpp b/engines/tinsel/scene.cpp
index 33d4e67d70..8f0f4771e3 100644
--- a/engines/tinsel/scene.cpp
+++ b/engines/tinsel/scene.cpp
@@ -106,6 +106,8 @@ struct ENTRANCE_STRUC {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
#ifdef DEBUG
static bool ShowPosition = false; // Set when showpos() has been called
#endif
@@ -368,6 +370,10 @@ void EndScene() {
*/
void PrimeBackground() {
// structure for playfields
+ // FIXME: Avoid non-const global vars
+ // TODO: We should simply merge this function with InitBackground
+ // in order to avoid the static var and the problems associate
+ // with it.
static PLAYFIELD playfield[] = {
{ // FIELD WORLD
NULL, // display list
@@ -390,7 +396,7 @@ void PrimeBackground() {
};
// structure for background
- static BACKGND backgnd = {
+ static const BACKGND backgnd = {
BLACK, // sky colour
Common::Point(0, 0), // initial world pos
Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), // scroll limits
diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h
index e17a6ab7a0..2ef7da1289 100644
--- a/engines/tinsel/scene.h
+++ b/engines/tinsel/scene.h
@@ -78,9 +78,9 @@ enum REEL {
typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS;
// amount to shift scene handles by
-#define SCNHANDLE_SHIFT (TinselV2 ? 25 : 23)
-#define OFFSETMASK (TinselV2 ? 0x01ffffffL : 0x007fffffL)
-#define HANDLEMASK (TinselV2 ? 0xFE000000L : 0xFF800000L)
+#define SCNHANDLE_SHIFT ((TinselV2 && !IsDemo) ? 25 : 23)
+#define OFFSETMASK ((TinselV2 && !IsDemo) ? 0x01ffffffL : 0x007fffffL)
+#define HANDLEMASK ((TinselV2 && !IsDemo) ? 0xFE000000L : 0xFF800000L)
void DoHailScene(SCNHANDLE scene);
diff --git a/engines/tinsel/sched.cpp b/engines/tinsel/sched.cpp
index d8a44944fc..939b5f4245 100644
--- a/engines/tinsel/sched.cpp
+++ b/engines/tinsel/sched.cpp
@@ -45,10 +45,10 @@ struct PROCESS_STRUC {
#include "common/pack-end.h" // END STRUCT PACKING
-CoroContext nullContext = NULL;
-
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static uint32 numSceneProcess;
static SCNHANDLE hSceneProcess;
@@ -77,6 +77,14 @@ Scheduler::Scheduler() {
}
Scheduler::~Scheduler() {
+ // Kill all running processes (i.e. free memory allocated for their state).
+ PROCESS *pProc = active->pNext;
+ while (pProc != NULL) {
+ delete pProc->state;
+ pProc->state = 0;
+ pProc = pProc->pNext;
+ }
+
free(processList);
processList = NULL;
@@ -126,7 +134,7 @@ void Scheduler::reset() {
* Shows the maximum number of process used at once.
*/
void Scheduler::printStats() {
- printf("%i process of %i used.\n", maxProcs, NUM_PROCESS);
+ debug("%i process of %i used", maxProcs, NUM_PROCESS);
}
#endif
@@ -392,6 +400,7 @@ void Scheduler::killProcess(PROCESS *pKillProc) {
(pRCfunction)(pKillProc);
delete pKillProc->state;
+ pKillProc->state = 0;
// Take the process out of the active chain list
pKillProc->pPrevious->pNext = pKillProc->pNext;
@@ -458,6 +467,7 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
(pRCfunction)(pProc);
delete pProc->state;
+ pProc->state = 0;
// make prev point to next to unlink pProc
pPrev->pNext = pProc->pNext;
@@ -752,6 +762,7 @@ void GlobalProcesses(uint32 numProcess, byte *pProcess) {
*/
void FreeGlobalProcesses() {
delete[] pGlobalProcess;
+ pGlobalProcess = 0;
numGlobalProcess = 0;
}
diff --git a/engines/tinsel/scn.cpp b/engines/tinsel/scn.cpp
index 0c89819ca0..326fd9e750 100644
--- a/engines/tinsel/scn.cpp
+++ b/engines/tinsel/scn.cpp
@@ -46,6 +46,8 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
byte *bptr = LockMem(handle);
uint32 *lptr = (uint32 *)bptr;
uint32 add;
+ bool bigEndian = (_vm->getFeatures() & GF_BIG_ENDIAN) != 0;
+ assert(!bigEndian); // Big endian data not yet supported
// Initial adjustmnet for Tinsel 1 chunk types
if ((TinselVersion != TINSEL_V2) && (chunk >= CHUNK_SCENE) &&
@@ -60,14 +62,17 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
chunk -= 0x2L;
while (1) {
- if (READ_LE_UINT32(lptr) == chunk)
+ if (READ_32(lptr) == chunk)
return (byte *)(lptr + 2);
++lptr;
- add = READ_LE_UINT32(lptr);
+ add = READ_32(lptr);
+
if (!add)
+ // End of file reached
return NULL;
+ // Move to next chunk
lptr = (uint32 *)(bptr + add);
}
}
diff --git a/engines/tinsel/scroll.cpp b/engines/tinsel/scroll.cpp
index 47c61a897e..3da970915f 100644
--- a/engines/tinsel/scroll.cpp
+++ b/engines/tinsel/scroll.cpp
@@ -49,6 +49,9 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
+
static int LeftScroll = 0, DownScroll = 0; // Number of iterations outstanding
static int scrollActor = 0;
diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp
index 6e8e736e14..7fab3624a4 100644
--- a/engines/tinsel/sound.cpp
+++ b/engines/tinsel/sound.cpp
@@ -37,6 +37,7 @@
#include "common/config-manager.h"
#include "common/endian.h"
#include "common/file.h"
+#include "common/memstream.h"
#include "common/system.h"
#include "sound/mixer.h"
@@ -104,7 +105,7 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// read the length of the sample
- uint32 sampleLen = _sampleStream.readUint32LE();
+ uint32 sampleLen = _sampleStream.readUint32();
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
@@ -263,7 +264,7 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// read the length of the sample
- uint32 sampleLen = _sampleStream.readUint32LE();
+ uint32 sampleLen = _sampleStream.readUint32();
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
@@ -276,12 +277,12 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p
// Skipping
for (int32 i = 0; i < sub; i++) {
- sampleLen = _sampleStream.readUint32LE();
+ sampleLen = _sampleStream.readUint32();
_sampleStream.skip(sampleLen);
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
}
- sampleLen = _sampleStream.readUint32LE();
+ sampleLen = _sampleStream.readUint32();
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
}
@@ -480,8 +481,8 @@ void SoundManager::setSFXVolumes(uint8 volume) {
* Opens and inits all sound sample files.
*/
void SoundManager::openSampleFiles() {
- // Floppy and demo versions have no sample files
- if (_vm->getFeatures() & GF_FLOPPY || _vm->getFeatures() & GF_DEMO)
+ // Floppy and demo versions have no sample files, except for the Discworld 2 demo
+ if (_vm->getFeatures() & GF_FLOPPY || (IsDemo && !TinselV2))
return;
TinselFile f;
diff --git a/engines/tinsel/strres.cpp b/engines/tinsel/strres.cpp
index 2416d6a8fa..aa303a5391 100644
--- a/engines/tinsel/strres.cpp
+++ b/engines/tinsel/strres.cpp
@@ -36,6 +36,8 @@
namespace Tinsel {
+// FIXME: Avoid non-const global vars
+
#ifdef DEBUG
// Diagnostic number
int newestString;
@@ -109,7 +111,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// Check whether the file is compressed or not - for compressed files the
// first long is the filelength and for uncompressed files it is the chunk
// identifier
- textLen = f.readUint32LE();
+ textLen = f.readUint32();
if (f.eos() || f.err())
error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
diff --git a/engines/tinsel/sysvar.cpp b/engines/tinsel/sysvar.cpp
index 1732207659..7003d34feb 100644
--- a/engines/tinsel/sysvar.cpp
+++ b/engines/tinsel/sysvar.cpp
@@ -42,6 +42,8 @@ extern int NewestSavedGame();
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
static int systemVars[SV_TOPVALID] = {
INV_1, // Default inventory
@@ -104,7 +106,7 @@ static int systemVars[SV_TOPVALID] = {
0 // ISV_GHOST_COLOUR
};
-static SCNHANDLE systemStrings[SS_MAX_VALID];
+static SCNHANDLE systemStrings[SS_MAX_VALID]; // FIXME: Avoid non-const global vars
//static bool bFlagNoBlocking = false;
diff --git a/engines/tinsel/text.cpp b/engines/tinsel/text.cpp
index c7c921b935..d2939281eb 100644
--- a/engines/tinsel/text.cpp
+++ b/engines/tinsel/text.cpp
@@ -107,7 +107,7 @@ int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
* @param mode Mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/
-OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
+OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
int xJustify; // x position of text after justification
int yOffset; // offset to next line of text
diff --git a/engines/tinsel/text.h b/engines/tinsel/text.h
index 664f0d207c..a849e286ec 100644
--- a/engines/tinsel/text.h
+++ b/engines/tinsel/text.h
@@ -98,7 +98,7 @@ struct TEXTOUT {
* @param mode mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/
-OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
+OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime = 0);
OBJECT *ObjectTextOutIndirect( // output a string of text
diff --git a/engines/tinsel/timers.cpp b/engines/tinsel/timers.cpp
index 9c7f709608..5f15cd9d3b 100644
--- a/engines/tinsel/timers.cpp
+++ b/engines/tinsel/timers.cpp
@@ -52,7 +52,7 @@ struct TIMER {
//----------------- LOCAL GLOBAL DATA --------------------
-static TIMER timers[MAX_TIMERS];
+static TIMER timers[MAX_TIMERS]; // FIXME: Avoid non-const global vars
//--------------------------------------------------------
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 766d4ed54a..40418dcc43 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -122,6 +122,8 @@ SCNHANDLE GetSceneHandle();
//----------------- GLOBAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
bool bEnableMenu;
static bool bInstantScroll = false;
@@ -172,7 +174,7 @@ enum MASTER_LIB_CODES {
HIGHEST_LIBCODE
};
-const MASTER_LIB_CODES DW1DEMO_CODES[] = {
+static const MASTER_LIB_CODES DW1DEMO_CODES[] = {
ACTORREF, ACTORXPOS, ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, AUXSCALE, BACKGROUND,
CAMERA, CONTROL, CONVERSATION, CONVTOPIC, HIGHEST_LIBCODE, CURSORXPOS, CURSORYPOS,
DECCONVW, DECCURSOR, DECTAGFONT, DECINVW, DECINV1, DECINV2, DECLEAD, DELICON,
@@ -186,7 +188,7 @@ const MASTER_LIB_CODES DW1DEMO_CODES[] = {
WALKTAG, RANDOM, TIMER
};
-const MASTER_LIB_CODES DW1_CODES[] = {
+static const MASTER_LIB_CODES DW1_CODES[] = {
ACTORATTR, ACTORDIRECTION, ACTORREF, ACTORSCALE, ACTORXPOS,
ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, ADDOPENINV, AUXSCALE,
BACKGROUND, CAMERA, CLOSEINVENTORY, CONTROL, CONVERSATION,
@@ -213,7 +215,44 @@ const MASTER_LIB_CODES DW1_CODES[] = {
HIGHEST_LIBCODE
};
-const MASTER_LIB_CODES DW2_CODES[] = {
+static const MASTER_LIB_CODES DW2DEMO_CODES[] = {
+ ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
+ ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
+ ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
+ BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
+ CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
+ CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
+ CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
+ DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
+ DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
+ DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
+ ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
+ FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, HASRESTARTED,
+ HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
+ HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, INSTANTSCROLL,
+ INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLGLOBALPROCESS,
+ KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
+ NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET, OTHEROBJECT, PAUSE, PLAY,
+ PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR,
+ POSTGLOBALPROCESS, POSTOBJECT, POSTPROCESS, POSTTAG, PRINT,
+ PRINTCURSOR, PRINTOBJ, PRINTTAG, QUITGAME, RANDOM, RESETIDLETIME,
+ RESTARTGAME, RESTORESCENE, RUNMODE, SAVESCENE, SAY, SAYAT,
+ SCALINGREELS, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS,
+ SENDACTOR, SENDGLOBALPROCESS, SENDOBJECT, SENDPROCESS, SENDTAG,
+ SETBRIGHTNESS, SETINVLIMIT, SETINVSIZE, SETLANGUAGE, SETPALETTE,
+ SETSYSTEMSTRING, SETSYSTEMVAR, SHELL, SHOWACTOR, SHOWBLOCK,
+ SHOWEFFECT, SHOWPATH, SHOWREFER, SHOWTAG, STAND, STANDTAG,
+ STARTGLOBALPROCESS, STARTPROCESS, STARTTIMER, STOPWALK, SUBTITLES,
+ SWALK, SYSTEMVAR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS,
+ TALK, TALKAT, TALKPALETTEINDEX, TALKRGB, TALKVIA, THISOBJECT,
+ THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
+ UNDIMMUSIC, UNHOOKSCENE, WAITFRAME, WAITKEY, WAITSCROLL, WAITTIME,
+ WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
+ WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY,
+ HIGHEST_LIBCODE
+};
+
+static const MASTER_LIB_CODES DW2_CODES[] = {
ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
@@ -255,6 +294,8 @@ const MASTER_LIB_CODES DW2_CODES[] = {
//----------------- LOCAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
// Saved cursor co-ordinates for control(on) to restore cursor position
// as it was at control(off).
// They are global so that MoveCursor(..) has a net effect if it
@@ -267,8 +308,6 @@ static int scrollNumber = 0; // used by scroll()
static bool bNotPointedRunning = false; // Used in Printobj and PrintObjPointed
-static COLORREF s_talkfontColor = 0;
-
//----------------- FORWARD REFERENCES --------------------
static int HeldObject();
@@ -390,9 +429,8 @@ static void ScrollMonitorProcess(CORO_PARAM, const void *param) {
* Poke supplied colour into the DAC queue.
*/
void SetTextPal(COLORREF col) {
- s_talkfontColor = col;
SetTalkColourRef(col);
- UpdateDACqueue(TalkColour(), 1, &s_talkfontColor);
+ UpdateDACqueue(TalkColour(), col);
}
/**
@@ -1912,7 +1950,7 @@ static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSust
if (TinselV2) {
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x - Loffset, y - Toffset, GetTagFontHandle(),
TXT_CENTRE, 0);
assert(_ctx->pText);
@@ -1925,7 +1963,7 @@ static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSust
} else if (bJapDoPrintText || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
int Loffset, Toffset; // Screen position
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, x - Loffset, y - Toffset,
TinselV2 ? GetTagFontHandle() : GetTalkFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // string produced NULL text
@@ -2089,7 +2127,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
else
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
- _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, _ctx->textx, _ctx->texty, GetTagFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // PrintObj() string produced NULL text
@@ -2141,7 +2179,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
// Re-display in the same place
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
- _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, _ctx->textx, _ctx->texty, GetTagFontHandle(),
TXT_CENTRE, 0);
assert(_ctx->pText);
@@ -2258,7 +2296,7 @@ static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
// Re-display in the same place
LoadStringRes(text, TextBufferAddr(), TBUFSZ);
- pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, textx, texty, GetTagFontHandle(), TXT_CENTRE);
assert(pText); // PrintObj() string produced NULL text
MultiSetZPosition(pText, Z_INV_ITEXT);
@@ -2551,7 +2589,7 @@ static void Scroll(CORO_PARAM, EXTREME extreme, int xp, int yp, int xIter, int y
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
} while (Loffset != _ctx->x || Toffset != _ctx->y);
} else if (TinselV2 && myEscape) {
- static SCROLL_MONITOR sm;
+ SCROLL_MONITOR sm;
// Scroll is escapable even though we're not waiting for it
sm.x = _ctx->x;
@@ -3327,7 +3365,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x
_ctx->y -= _ctx->Toffset;
}
- _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset,
GetTalkFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // talk() string produced NULL text;
@@ -3376,7 +3414,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x
// Kick off the sample now (perhaps with a delay)
if (bNoPause)
bNoPause = false;
- else
+ else if (!IsDemo)
CORO_SLEEP(SysVar(SV_SPEECHDELAY));
//SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
@@ -4208,6 +4246,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
int libCode;
if (TinselV0) libCode = DW1DEMO_CODES[operand];
else if (!TinselV2) libCode = DW1_CODES[operand];
+ else if (_vm->getFeatures() & GF_DEMO) libCode = DW2DEMO_CODES[operand];
else libCode = DW2_CODES[operand];
debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
@@ -4907,9 +4946,6 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
else {
Play(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, false,
pic->event, pic->hPoly, pic->idActor);
-
- if (coroParam)
- return 0;
}
return -4;
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index f16b5f9100..0e1001b398 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -36,6 +36,8 @@
#include "common/serializer.h"
#include "common/stream.h"
+#include "backends/audiocd/audiocd.h"
+
#include "engines/util.h"
#include "graphics/cursorman.h"
@@ -79,6 +81,7 @@ namespace Tinsel {
// In BG.CPP
extern void SetDoFadeIn(bool tf);
extern void DropBackground();
+extern const BACKGND *pCurBgnd;
// In CURSOR.CPP
extern void CursorProcess(CORO_PARAM, const void *);
@@ -95,6 +98,8 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition);
//----------------- GLOBAL GLOBAL DATA --------------------
+// FIXME: Avoid non-const global vars
+
bool bRestart = false;
bool bHasRestarted = false;
bool loadingFromGMM = false;
@@ -631,6 +636,7 @@ void RestoreMasterProcess(INT_CONTEXT *pic) {
}
// FIXME: CountOut is used by ChangeScene
+// FIXME: Avoid non-const global vars
static int CountOut = 1; // == 1 for immediate start of first scene
/**
@@ -716,15 +722,15 @@ void LoadBasicChunks() {
// CHUNK_TOTAL_ACTORS seems to be missing in the released version, hard coding a value
// TODO: Would be nice to just change 511 to MAX_SAVED_ALIVES
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_ACTORS);
- RegisterActors((cptr != NULL) ? READ_LE_UINT32(cptr) : 511);
+ RegisterActors((cptr != NULL) ? READ_32(cptr) : 511);
// CHUNK_TOTAL_GLOBALS seems to be missing in some versions.
// So if it is missing, set a reasonably high value for the number of globals.
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_GLOBALS);
- RegisterGlobals((cptr != NULL) ? READ_LE_UINT32(cptr) : 512);
+ RegisterGlobals((cptr != NULL) ? READ_32(cptr) : 512);
cptr = FindChunk(INV_OBJ_SCNHANDLE, CHUNK_TOTAL_OBJECTS);
- numObjects = (cptr != NULL) ? READ_LE_UINT32(cptr) : 0;
+ numObjects = (cptr != NULL) ? READ_32(cptr) : 0;
cptr = FindChunk(INV_OBJ_SCNHANDLE, CHUNK_OBJECTS);
@@ -757,7 +763,7 @@ void LoadBasicChunks() {
// CdPlay() stuff
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_CDPLAY_HANDLE);
assert(cptr);
- uint32 playHandle = READ_LE_UINT32(cptr);
+ uint32 playHandle = READ_32(cptr);
assert(playHandle < 512);
SetCdPlayHandle(playHandle);
}
@@ -1032,6 +1038,9 @@ Common::Error TinselEngine::run() {
// Write configuration
_vm->_config->writeToDisk();
+ EndScene();
+ pCurBgnd = NULL;
+
return Common::kNoError;
}
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index ed70979349..b4f77d18f2 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -48,7 +48,7 @@
*
* Status of this engine: Complete
*
- * Supported games:
+ * Games using this engine:
* - Discworld
* - Discworld 2: Missing Presumed ...!?
*/
@@ -81,7 +81,9 @@ enum TinselGameFeatures {
// None of these defined -> 1 language, in ENGLISH.TXT
GF_USE_3FLAGS = 1 << 6, // French, German, Spanish
GF_USE_4FLAGS = 1 << 7, // French, German, Spanish, Italian
- GF_USE_5FLAGS = 1 << 8 // All 5 flags
+ GF_USE_5FLAGS = 1 << 8, // All 5 flags
+
+ GF_BIG_ENDIAN = 1 << 9
};
/**
@@ -139,6 +141,12 @@ typedef bool (*KEYFPTR)(const Common::KeyState &);
#define TinselV1 (TinselVersion == TINSEL_V1)
#define TinselV2 (TinselVersion == TINSEL_V2)
#define TinselV1PSX (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformPSX)
+#define TinselV1Mac (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformMacintosh)
+
+#define IsDemo (_vm->getFeatures() & GF_DEMO)
+
+#define READ_16(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT16(v) : READ_LE_UINT16(v))
+#define READ_32(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT32(v) : READ_LE_UINT32(v))
// Global reference to the TinselEngine object
extern TinselEngine *_vm;
@@ -183,6 +191,7 @@ public:
uint32 getFeatures() const;
Common::Language getLanguage() const;
uint16 getVersion() const;
+ uint32 getFlags() const;
Common::Platform getPlatform() const;
const char *getSampleIndex(LANGUAGE lang);
diff --git a/engines/tinsel/token.cpp b/engines/tinsel/token.cpp
index 2acf3c073f..fe152edcaa 100644
--- a/engines/tinsel/token.cpp
+++ b/engines/tinsel/token.cpp
@@ -37,7 +37,7 @@ struct Token {
PROCESS *proc;
};
-static Token tokens[NUMTOKENS];
+static Token tokens[NUMTOKENS]; // FIXME: Avoid non-const global vars
/**
diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp
index 157422cd9d..00cfdad9cc 100644
--- a/engines/toon/anim.cpp
+++ b/engines/toon/anim.cpp
@@ -58,6 +58,7 @@ bool Animation::loadAnimation(Common::String file) {
uint8 *currentData = fileData + 68;
if (_paletteEntries) {
if (paletteSize) {
+ delete[] _palette;
_palette = new uint8[paletteSize];
memcpy(_palette, currentData, paletteSize);
currentData += paletteSize;
@@ -74,6 +75,7 @@ bool Animation::loadAnimation(Common::String file) {
if (READ_LE_UINT32(finalBuffer) == 0x12345678) {
uint8 *data = finalBuffer;
+ delete[] _frames;
_frames = new AnimationFrame[_numFrames];
for (int32 e = 0; e < _numFrames; e++) {
if (READ_LE_UINT32(data) != 0x12345678)
@@ -95,20 +97,26 @@ bool Animation::loadAnimation(Common::String file) {
} else {
_frames[e]._ref = -1;
_frames[e]._data = new uint8[decompressedSize];
- decompressLZSS(imageData, _frames[e]._data, decompressedSize);
+ if (compressedSize < decompressedSize) {
+ decompressLZSS(imageData, _frames[e]._data, decompressedSize);
+ } else {
+ memcpy(_frames[e]._data, imageData, compressedSize);
+ }
}
data += headerSize + compressedSize;
}
}
+ _vm->resources()->purgeFileData();
delete[] finalBuffer;
return true;
}
Animation::Animation(ToonEngine *vm) : _vm(vm) {
- _palette = 0;
- _frames = 0;
+ _palette = NULL;
+ _numFrames = 0;
+ _frames = NULL;
}
Animation::~Animation() {
@@ -140,9 +148,26 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int
int32 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int32 rectY = _frames[frame]._y2 - _frames[frame]._y1;
+ int32 offsX = 0;
+ int32 offsY = 0;
- if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0))
+ if (xx + _x1 + _frames[frame]._x1 < 0) {
+ offsX = -(xx + _x1 + _frames[frame]._x1);
+ }
+
+ if (offsX >= rectX)
return;
+ else
+ rectX -= offsX;
+
+ if (yy + _y1 + _frames[frame]._y1 < 0) {
+ offsY = -(yy + _y1 + _frames[frame]._y1);
+ }
+
+ if (offsY >= rectY)
+ return;
+ else
+ rectY -= offsY;
if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w)
rectX = surface.w - xx - _x1 - _frames[frame]._x1;
@@ -157,8 +182,8 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int
return;
int32 destPitch = surface.pitch;
- uint8 *srcRow = _frames[frame]._data;
- uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1);
+ uint8 *srcRow = _frames[frame]._data + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY;
+ uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1 + offsY) * destPitch + (xx + _x1 + _frames[frame]._x1 + offsX);
for (int32 y = 0; y < rectY; y++) {
uint8 *cur = curRow;
uint8 *c = srcRow + y * (_frames[frame]._x2 - _frames[frame]._x1);
@@ -408,6 +433,7 @@ AnimationInstance::AnimationInstance(ToonEngine *vm, AnimationInstanceType type)
_playing = false;
_rangeEnd = 0;
_useMask = false;
+ _alignBottom = false;
_rangeStart = 0;
_scale = 1024;
_x = 0;
@@ -416,6 +442,7 @@ AnimationInstance::AnimationInstance(ToonEngine *vm, AnimationInstanceType type)
_layerZ = 0;
}
+
void AnimationInstance::render() {
debugC(5, kDebugAnim, "render()");
if (_visible && _animation) {
@@ -426,11 +453,22 @@ void AnimationInstance::render() {
if (frame >= _animation->_numFrames)
frame = _animation->_numFrames - 1;
+ int32 x = _x;
+ int32 y = _y;
+
+ if (_alignBottom) {
+ int32 offsetX = (_animation->_x2 - _animation->_x1) / 2 * (_scale - 1024);
+ int32 offsetY = (_animation->_y2 - _animation->_y1) * (_scale - 1024);
+
+ x -= offsetX >> 10;
+ y -= offsetY >> 10;
+ }
+
if (_useMask) {
//if (_scale == 100) { // 100% scale
// _animation->drawFrameWithMask(_vm->getMainSurface(), _currentFrame, _x, _y, _z, _vm->getMask());
//} else {
- _animation->drawFrameWithMaskAndScale(_vm->getMainSurface(), frame, _x, _y, _z, _vm->getMask(), _scale);
+ _animation->drawFrameWithMaskAndScale(_vm->getMainSurface(), frame, x, y, _z, _vm->getMask(), _scale);
//}
} else {
_animation->drawFrame(_vm->getMainSurface(), frame, _x, _y);
@@ -517,9 +555,10 @@ void AnimationInstance::setVisible(bool visible) {
_visible = visible;
}
-void AnimationInstance::setScale(int32 scale) {
+void AnimationInstance::setScale(int32 scale, bool align) {
debugC(4, kDebugAnim, "setScale(%d)", scale);
_scale = scale;
+ _alignBottom = align;
}
void AnimationInstance::setUseMask(bool useMask) {
diff --git a/engines/toon/anim.h b/engines/toon/anim.h
index 7bf633220c..8c5d63dd41 100644
--- a/engines/toon/anim.h
+++ b/engines/toon/anim.h
@@ -102,7 +102,7 @@ public:
void forceFrame(int32 position);
void setPosition(int32 x, int32 y, int32 z, bool relative = false);
Animation *getAnimation() const { return _animation; }
- void setScale(int32 scale);
+ void setScale(int32 scale, bool align = false);
void setVisible(bool visible);
bool getVisible() const { return _visible; }
void setUseMask(bool useMask);
@@ -150,6 +150,7 @@ protected:
bool _playing;
bool _looping;
bool _visible;
+ bool _alignBottom;
ToonEngine *_vm;
};
@@ -171,6 +172,7 @@ protected:
class SceneAnimation {
public:
+ AnimationInstance *_originalAnimInstance;
AnimationInstance *_animInstance;
Animation *_animation;
int32 _id;
@@ -186,6 +188,7 @@ public:
EMCState _state;
uint32 _lastTimer;
bool _frozen;
+ bool _frozenForConversation;
bool _active;
};
diff --git a/engines/toon/audio.cpp b/engines/toon/audio.cpp
index 496d626201..d0e9b168ae 100644
--- a/engines/toon/audio.cpp
+++ b/engines/toon/audio.cpp
@@ -24,6 +24,8 @@
*/
#include "toon/audio.h"
+#include "common/memstream.h"
+#include "common/substream.h"
namespace Toon {
@@ -44,10 +46,46 @@ static int ADPCM_table[89] = {
AudioManager::AudioManager(ToonEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
for (int32 i = 0; i < 16; i++)
- _channels[i] = 0;
+ _channels[i] = NULL;
+
+ for (int32 i = 0; i < 4; i++)
+ _audioPacks[i] = NULL;
+
+ for (int32 i = 0; i < 4; i++) {
+ _ambientSFXs[i]._delay = 0;
+ _ambientSFXs[i]._enabled = false;
+ _ambientSFXs[i]._id = -1;
+ _ambientSFXs[i]._channel = -1;
+ _ambientSFXs[i]._lastTimer = 0;
+ _ambientSFXs[i]._volume = 255;
+ }
+
+ _voiceMuted = false;
+ _musicMuted = false;
+ _sfxMuted = false;
}
AudioManager::~AudioManager(void) {
+ _mixer->stopAll();
+ for (int32 i = 0; i < 4; i++) {
+ closeAudioPack(i);
+ }
+}
+
+void AudioManager::muteMusic(bool muted) {
+ setMusicVolume(muted ? 0 : 255);
+ _musicMuted = muted;
+}
+
+void AudioManager::muteVoice(bool muted) {
+ if(voiceStillPlaying() && _channels[2]) {
+ _channels[2]->setVolume(muted ? 0 : 255);
+ }
+ _voiceMuted = muted;
+}
+
+void AudioManager::muteSfx(bool muted) {
+ _sfxMuted = muted;
}
void AudioManager::removeInstance(AudioStreamInstance *inst) {
@@ -55,7 +93,7 @@ void AudioManager::removeInstance(AudioStreamInstance *inst) {
for (int32 i = 0; i < 16; i++) {
if (inst == _channels[i])
- _channels[i] = 0;
+ _channels[i] = NULL;
}
}
@@ -63,7 +101,7 @@ void AudioManager::playMusic(Common::String dir, Common::String music) {
debugC(1, kDebugAudio, "playMusic(%s, %s)", dir.c_str(), music.c_str());
// two musics can be played at same time
- Common::String path = Common::String::printf("act%d/%s/%s.mus", _vm->state()->_currentChapter, dir.c_str(), music.c_str());
+ Common::String path = Common::String::format("act%d/%s/%s.mus", _vm->state()->_currentChapter, dir.c_str(), music.c_str());
if (_currentMusicName == music)
return;
@@ -95,10 +133,9 @@ void AudioManager::playMusic(Common::String dir, Common::String music) {
_currentMusicChannel = 0;
}
-
- //if (!_channels[_currentMusicChannel])
- // delete _channels[_currentMusicChannel];
- _channels[_currentMusicChannel] = new AudioStreamInstance(this, _mixer, srs, true);
+ // no need to delete instance here it will automatically deleted by the mixer is done with it
+ _channels[_currentMusicChannel] = new AudioStreamInstance(this, _mixer, srs, true, true);
+ _channels[_currentMusicChannel]->setVolume(_musicMuted ? 0 : 255);
_channels[_currentMusicChannel]->play(true, Audio::Mixer::kMusicSoundType);
}
@@ -123,12 +160,14 @@ void AudioManager::playVoice(int32 id, bool genericVoice) {
else
stream = _audioPacks[1]->getStream(id);
- _channels[2] = new AudioStreamInstance(this, _mixer, stream);
+ // no need to delete channel 2, it will be deleted by the mixer when the stream is finished
+ _channels[2] = new AudioStreamInstance(this, _mixer, stream, false, true);
_channels[2]->play(false, Audio::Mixer::kSpeechSoundType);
+ _channels[2]->setVolume(_voiceMuted ? 0 : 255);
}
-void AudioManager::playSFX(int32 id, int32 volume , bool genericSFX) {
+int32 AudioManager::playSFX(int32 id, int volume , bool genericSFX) {
debugC(4, kDebugAudio, "playSFX(%d, %d)", id, (genericSFX) ? 1 : 0);
// find a free SFX channel
@@ -140,14 +179,24 @@ void AudioManager::playSFX(int32 id, int32 volume , bool genericSFX) {
stream = _audioPacks[3]->getStream(id, true);
if (stream->size() == 0)
- return;
+ return -1;
for (int32 i = 3; i < 16; i++) {
if (!_channels[i]) {
- _channels[i] = new AudioStreamInstance(this, _mixer, stream);
+ _channels[i] = new AudioStreamInstance(this, _mixer, stream, false, true);
_channels[i]->play(false, Audio::Mixer::kSFXSoundType);
- _channels[i]->setVolume(volume);
- break;
+ _channels[i]->setVolume(_sfxMuted ? 0 : volume);
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void AudioManager::stopAllSfxs() {
+ for (int32 i = 3; i < 16; i++) {
+ if (_channels[i] && _channels[i]->isPlaying()) {
+ _channels[i]->stop(false);
}
}
}
@@ -159,9 +208,16 @@ void AudioManager::stopCurrentVoice() {
_channels[2]->stop(false);
}
+
+void AudioManager::closeAudioPack(int32 id) {
+ delete _audioPacks[id];
+ _audioPacks[id] = NULL;
+}
+
bool AudioManager::loadAudioPack(int32 id, Common::String indexFile, Common::String packFile) {
debugC(4, kDebugAudio, "loadAudioPack(%d, %s, %s)", id, indexFile.c_str(), packFile.c_str());
+ closeAudioPack(id);
_audioPacks[id] = new AudioStreamPackage(_vm);
return _audioPacks[id]->loadAudioPackage(indexFile, packFile);
}
@@ -183,17 +239,13 @@ void AudioManager::stopMusic() {
if (_channels[1])
_channels[1]->stop(true);
}
-AudioStreamInstance::~AudioStreamInstance() {
- if (_man)
- _man->removeInstance(this);
-}
-AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream , bool looping) {
+AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream , bool looping, bool deleteFileStreamAtEnd) {
_compBufferSize = 0;
- _buffer = 0;
+ _buffer = NULL;
_bufferMaxSize = 0;
_mixer = mixer;
- _compBuffer = 0;
+ _compBuffer = NULL;
_bufferOffset = 0;
_lastADPCMval1 = 0;
_lastADPCMval2 = 0;
@@ -208,6 +260,7 @@ AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer,
_man = man;
_looping = looping;
_musicAttenuation = 1000;
+ _deleteFileStream = deleteFileStreamAtEnd;
// preload one packet
if (_totalSize > 0) {
@@ -218,9 +271,24 @@ AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer,
}
}
-int32 AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) {
+AudioStreamInstance::~AudioStreamInstance() {
+ delete[] _buffer;
+ delete[] _compBuffer;
+
+ if (_man)
+ _man->removeInstance(this);
+
+ if (_deleteFileStream) {
+ delete _file;
+ }
+}
+
+int AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) {
debugC(5, kDebugAudio, "readBuffer(buffer, %d)", numSamples);
+ if(_stopped)
+ return 0;
+
handleFade(numSamples);
int32 leftSamples = numSamples;
int32 destOffset = 0;
@@ -264,15 +332,13 @@ bool AudioStreamInstance::readPacket() {
_file->readSint32LE();
if (numCompressedBytes > _compBufferSize) {
- if (_compBuffer)
- delete[] _compBuffer;
+ delete[] _compBuffer;
_compBufferSize = numCompressedBytes;
_compBuffer = new uint8[_compBufferSize];
}
if (numDecompressedBytes > _bufferMaxSize) {
- if (_buffer)
- delete [] _buffer;
+ delete [] _buffer;
_bufferMaxSize = numDecompressedBytes;
_buffer = new int16[numDecompressedBytes];
}
@@ -349,11 +415,11 @@ void AudioStreamInstance::play(bool fade, Audio::Mixer::SoundType soundType) {
_fadeTime = 0;
_soundType = soundType;
_musicAttenuation = 1000; // max volume
- _mixer->playStream(soundType, &_handle, this);
+ _mixer->playStream(soundType, &_handle, this, -1);
handleFade(0);
}
-void AudioStreamInstance::handleFade(int numSamples) {
+void AudioStreamInstance::handleFade(int32 numSamples) {
debugC(5, kDebugAudio, "handleFade(%d)", numSamples);
// Fading enabled only for music
@@ -395,9 +461,7 @@ void AudioStreamInstance::handleFade(int numSamples) {
_musicAttenuation = 1000;
}
-
_mixer->setChannelVolume(_handle, finalVolume * _musicAttenuation / 1000);
-
}
void AudioStreamInstance::stop(bool fade /*= false*/) {
@@ -426,11 +490,13 @@ void AudioStreamInstance::setVolume(int32 volume) {
}
AudioStreamPackage::AudioStreamPackage(ToonEngine *vm) : _vm(vm) {
- _indexBuffer = 0;
+ _indexBuffer = NULL;
+ _file = NULL;
}
AudioStreamPackage::~AudioStreamPackage() {
delete[] _indexBuffer;
+ delete _file;
}
bool AudioStreamPackage::loadAudioPackage(Common::String indexFile, Common::String streamFile) {
@@ -442,7 +508,6 @@ bool AudioStreamPackage::loadAudioPackage(Common::String indexFile, Common::Stri
return false;
delete[] _indexBuffer;
-
_indexBuffer = new uint32[size / 4];
memcpy(_indexBuffer, fileData, size);
@@ -467,7 +532,7 @@ Common::SeekableReadStream *AudioStreamPackage::getStream(int32 id, bool ownMemo
int32 size = 0;
getInfo(id, &offset, &size);
if (ownMemory) {
- byte *memory = new byte[size];
+ byte *memory = (byte*)malloc(size);
_file->seek(offset);
_file->read(memory, size);
return new Common::MemoryReadStream(memory, size, DisposeAfterUse::YES);
@@ -476,5 +541,94 @@ Common::SeekableReadStream *AudioStreamPackage::getStream(int32 id, bool ownMemo
}
}
+void AudioManager::startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume)
+{
+ int32 found = -1;
+ for (int32 i = 0; i < 4; i++) {
+ if (!_ambientSFXs[i]._enabled) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found < 0)
+ return;
+
+ _ambientSFXs[found]._lastTimer = _vm->getOldMilli() - 1;
+ _ambientSFXs[found]._delay = delay;
+ _ambientSFXs[found]._enabled = true;
+ _ambientSFXs[found]._mode = mode;
+ _ambientSFXs[found]._volume = volume;
+ _ambientSFXs[found]._id = id;
+ updateAmbientSFX();
+
+}
+
+void AudioManager::setAmbientSFXVolume(int32 id, int volume) {
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_id == id && ambient->_enabled) {
+ ambient->_volume = volume;
+ if (ambient->_channel >= 0 && _channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) {
+ _channels[ambient->_channel]->setVolume(volume);
+ }
+ break;
+ }
+ }
+}
+
+void AudioManager::killAmbientSFX(int32 id)
+{
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_id == id && ambient->_enabled) {
+ ambient->_enabled = false;
+ ambient->_id = -1;
+
+ if (_channels[ambient->_channel]) {
+ _channels[ambient->_channel]->stop(false);
+ }
+ }
+
+ }
+}
+
+void AudioManager::killAllAmbientSFX()
+{
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_enabled) {
+ ambient->_enabled = false;
+ ambient->_id = -1;
+ if (ambient->_channel >= 0 && _channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) {
+ _channels[ambient->_channel]->stop(false);
+ }
+ ambient->_channel = -1;
+ }
+ }
+}
+
+void AudioManager::updateAmbientSFX()
+{
+ if (_vm->getMoviePlayer()->isPlaying()) return;
+
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_enabled && (ambient->_channel < 0 || !(_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()))) {
+ if(ambient->_mode == 1) {
+ if (_vm->randRange(0, 32767) < ambient->_delay) {
+ ambient->_channel = playSFX(ambient->_id, ambient->_volume, false);
+ }
+ } else {
+ if (_vm->getOldMilli() > ambient->_lastTimer) {
+ ambient->_channel = playSFX(ambient->_id, ambient->_volume, false);
+ ambient->_lastTimer = _vm->getOldMilli(); // + 60 * _vm->getTickLength() * ambient->_delay;
+ }
+ }
+ }
+ }
+}
+
+
} // End of namespace Toon
diff --git a/engines/toon/audio.h b/engines/toon/audio.h
index 5a8274e086..e8c6c44e03 100644
--- a/engines/toon/audio.h
+++ b/engines/toon/audio.h
@@ -37,7 +37,7 @@ class AudioManager;
class AudioStreamInstance : public Audio::AudioStream {
public:
- AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream, bool looping = false);
+ AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream, bool looping = false, bool deleteFileStreamAtEnd = false);
~AudioStreamInstance();
void play(bool fade = false, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
void stop(bool fade = false);
@@ -54,7 +54,7 @@ public:
void setVolume(int32 volume);
protected:
- int32 readBuffer(int16 *buffer, const int numSamples);
+ int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const {
return false;
}
@@ -92,6 +92,7 @@ protected:
bool _looping;
int32 _volume;
int32 _musicAttenuation;
+ bool _deleteFileStream;
};
class AudioStreamPackage {
@@ -109,23 +110,46 @@ protected:
ToonEngine *_vm;
};
+struct AudioAmbientSFX {
+ int32 _id;
+ int32 _volume;
+ int32 _lastTimer;
+ int32 _delay;
+ int32 _mode;
+ int32 _channel;
+ bool _enabled;
+};
+
class AudioManager {
public:
void removeInstance(AudioStreamInstance *inst); // called by destructor
AudioManager(ToonEngine *vm, Audio::Mixer *mixer);
- ~AudioManager(void);
+ ~AudioManager();
bool voiceStillPlaying();
void playMusic(Common::String dir, Common::String music);
void playVoice(int32 id, bool genericVoice);
- void playSFX(int32 id, int volume, bool genericSFX);
+ int32 playSFX(int32 id, int volume, bool genericSFX);
void stopCurrentVoice();
+ void stopAllSfxs();
void setMusicVolume(int32 volume);
void stopMusic();
-
-
+ void muteVoice(bool mute);
+ void muteMusic(bool mute);
+ void muteSfx(bool mute);
+ bool isVoiceMuted() { return _voiceMuted; }
+ bool isMusicMuted() { return _musicMuted; }
+ bool isSfxMuted() { return _sfxMuted; }
+
+ void startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume);
+ void killAmbientSFX(int32 id);
+ void killAllAmbientSFX();
+ void updateAmbientSFX();
+ void setAmbientSFXVolume(int32 id, int volume);
+
+ void closeAudioPack(int32 id);
bool loadAudioPack(int32 id, Common::String indexFile, Common::String packFile);
AudioStreamInstance *_channels[16]; // 0-1 : music
@@ -140,6 +164,13 @@ public:
Common::String _currentMusicName;
ToonEngine *_vm;
Audio::Mixer *_mixer;
+
+protected:
+ bool _voiceMuted;
+ bool _musicMuted;
+ bool _sfxMuted;
+
+ AudioAmbientSFX _ambientSFXs[4];
};
} // End of namespace Toon
diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp
index 50913f89c7..1a63136c61 100644
--- a/engines/toon/character.cpp
+++ b/engines/toon/character.cpp
@@ -31,22 +31,25 @@
namespace Toon {
Character::Character(ToonEngine *vm) : _vm(vm) {
- _animationInstance = 0;
- _shadowAnimationInstance = 0;
- _shadowAnim = 0;
+ _animationInstance = NULL;
+ _shadowAnimationInstance = NULL;
_x = 0;
_y = 0;
_z = 0;
_finalX = 0;
_finalY = 0;
- _specialAnim = 0;
_sceneAnimationId = -1;
- _idleAnim = 0;
- _walkAnim = 0;
- _talkAnim = 0;
+
+ _walkAnim = NULL;
+ _idleAnim = NULL;
+ _talkAnim = NULL;
+ _shadowAnim = NULL;
+ _specialAnim = NULL;
+
_facing = 0;
_flags = 0;
_animFlags = 0;
+ _isTalking = false;
_id = 0;
_scale = 1024;
_blockingWalk = false;
@@ -63,17 +66,79 @@ Character::Character(ToonEngine *vm) : _vm(vm) {
}
Character::~Character(void) {
+ delete _animationInstance;
+ delete _shadowAnimationInstance;
+
+ delete _walkAnim;
+ delete _idleAnim;
+ delete _talkAnim;
+ delete _shadowAnim;
+ delete _specialAnim;
}
void Character::init() {
+}
+void Character::forceFacing( int32 facing ) {
+ debugC(4, kDebugCharacter, "forceFacing(%d)", facing);
+ _facing = facing;
}
void Character::setFacing(int32 facing) {
debugC(4, kDebugCharacter, "setFacing(%d)", facing);
+
+ if (facing == _facing)
+ return;
+
+ if (_blockingWalk) {
+ _flags |= 2;
+
+ int32 dir = 0;
+
+ _lastWalkTime = _vm->getSystem()->getMillis();
+ if ((_facing - facing + 8) % 8 > (facing - _facing + 8) % 8)
+ dir = 1;
+ else
+ dir = -1;
+
+ while (_facing != facing) {
+
+ int32 elapsedTime = _vm->getOldMilli() - _lastWalkTime;
+ while (elapsedTime > _vm->getTickLength() * 3 && _facing != facing) {
+ _facing += dir;
+
+ while (_facing >= 8)
+ _facing -= 8;
+ while (_facing < 0)
+ _facing += 8;
+
+ elapsedTime -= _vm->getTickLength() * 3;
+ _lastWalkTime = _vm->getOldMilli();
+ }
+
+ if (_currentPathNode == 0)
+ playStandingAnim();
+ else
+ playWalkAnim(0,0);
+ _vm->doFrame();
+ };
+
+ _flags &= ~2;
+ }
+
+
_facing = facing;
}
+void Character::forcePosition(int32 x, int32 y) {
+
+ debugC(5, kDebugCharacter, "forcePosition(%d, %d)", x, y);
+
+ setPosition(x,y);
+ _finalX = x;
+ _finalY = y;
+}
+
void Character::setPosition(int32 x, int32 y) {
debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
@@ -96,8 +161,8 @@ bool Character::walkTo(int32 newPosX, int32 newPosY) {
_vm->getPathFinding()->resetBlockingRects();
if (_id == 1) {
- int32 sizeX = MAX(5, 40 * _vm->getDrew()->getScale() / 1024);
- int32 sizeY = MAX(2, 20 * _vm->getDrew()->getScale() / 1024);
+ int32 sizeX = MAX<int32>(5, 40 * _vm->getDrew()->getScale() / 1024);
+ int32 sizeY = MAX<int32>(2, 20 * _vm->getDrew()->getScale() / 1024);
_vm->getPathFinding()->addBlockingEllipse(_vm->getDrew()->getFinalX(), _vm->getDrew()->getFinalY(), sizeX, sizeY);
}
@@ -118,21 +183,25 @@ bool Character::walkTo(int32 newPosX, int32 newPosY) {
_currentPathNodeCount = _vm->getPathFinding()->getPathNodeCount();
_currentPathNode = 0;
stopSpecialAnim();
- _flags |= 0x1;
+
_lastWalkTime = _vm->getSystem()->getMillis();
_numPixelToWalk = 0;
+ _flags |= 0x1;
+
if (_blockingWalk) {
while ((_x != newPosX || _y != newPosY) && _currentPathNode < _currentPathNodeCount && !_vm->shouldQuitGame()) {
if (_currentPathNode < _currentPathNodeCount - 10) {
- int32 delta = MIN(10, _currentPathNodeCount - _currentPathNode);
+ int32 delta = MIN<int32>(10, _currentPathNodeCount - _currentPathNode);
int32 dx = _currentPathX[_currentPathNode+delta] - _x;
int32 dy = _currentPathY[_currentPathNode+delta] - _y;
setFacing(getFacingFromDirection(dx, dy));
playWalkAnim(0, 0);
}
+
+
// in 1/1000 pixels
_numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024;
_lastWalkTime = _vm->getSystem()->getMillis();
@@ -201,27 +270,21 @@ int32 Character::getFacing() {
bool Character::loadWalkAnimation(Common::String animName) {
debugC(1, kDebugCharacter, "loadWalkAnimation(%s)", animName.c_str());
- if (_walkAnim)
- delete _walkAnim;
-
+ delete _walkAnim;
_walkAnim = new Animation(_vm);
return _walkAnim->loadAnimation(animName);
}
bool Character::loadIdleAnimation(Common::String animName) {
debugC(1, kDebugCharacter, "loadIdleAnimation(%s)", animName.c_str());
- if (_idleAnim)
- delete _idleAnim;
-
+ delete _idleAnim;
_idleAnim = new Animation(_vm);
return _idleAnim->loadAnimation(animName);
}
bool Character::loadTalkAnimation(Common::String animName) {
debugC(1, kDebugCharacter, "loadTalkAnimation(%s)", animName.c_str());
- if (_talkAnim)
- delete _talkAnim;
-
+ delete _talkAnim;
_talkAnim = new Animation(_vm);
return _talkAnim->loadAnimation(animName);
}
@@ -231,7 +294,11 @@ bool Character::setupPalette() {
}
void Character::playStandingAnim() {
+}
+void Character::updateTimers(int32 relativeAdd) {
+ _nextIdleTime += relativeAdd;
+ _lastWalkTime += relativeAdd;
}
void Character::stopSpecialAnim() {
@@ -239,13 +306,13 @@ void Character::stopSpecialAnim() {
// Strangerke - Commented (not used)
#if 0
if (_animSpecialId != _animSpecialDefaultId)
- delete anim
+ delete anim;
#endif
if (_animScriptId != -1)
- _vm->getSceneAnimationScript(_animScriptId)->_frozen = false;
+ _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = false;
- if (_sceneAnimationId != -1)
- _animationInstance->setAnimation(_vm->getSceneAnimation(_sceneAnimationId)->_animation);
+ //if (_sceneAnimationId != -1)
+ // _animationInstance->setAnimation(_vm->getSceneAnimation(_sceneAnimationId)->_animation);
bool needStandingAnim = (_animFlags & 0x40) != 0;
@@ -265,7 +332,7 @@ void Character::update(int32 timeIncrement) {
if ((_flags & 0x1) && _currentPathNodeCount > 0) {
if (_currentPathNode < _currentPathNodeCount) {
if (_currentPathNode < _currentPathNodeCount - 10) {
- int32 delta = MIN(10, _currentPathNodeCount - _currentPathNode);
+ int32 delta = MIN<int32>(10, _currentPathNodeCount - _currentPathNode);
int32 dx = _currentPathX[_currentPathNode+delta] - _x;
int32 dy = _currentPathY[_currentPathNode+delta] - _y;
setFacing(getFacingFromDirection(dx, dy));
@@ -342,11 +409,13 @@ void Character::update(int32 timeIncrement) {
#endif
if (_animScriptId != -1)
- _vm->getSceneAnimationScript(_animScriptId)->_frozen = true;
-
+ _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true;
+
+
// TODO setup backup //
_animFlags |= 0x10;
+ _animationInstance->setAnimation(_specialAnim);
_animationInstance->setFrame(0);
_time = _vm->getOldMilli() + 8 * _vm->getTickLength();
}
@@ -354,14 +423,11 @@ void Character::update(int32 timeIncrement) {
}
if ((_animFlags & 3) == 2) {
- if (_vm->getCurrentLineToSay() != _lineToSayId || !_vm->getAudioManager()->voiceStillPlaying()) // || (_flags & 8)) && _vm->getAudioManager()->voiceStillPlaying())
+ if ((((_animFlags & 8) == 8) && _vm->getCurrentLineToSay() != _lineToSayId) || !_vm->getAudioManager()->voiceStillPlaying()) // || (_flags & 8)) && _vm->getAudioManager()->voiceStillPlaying())
_animFlags |= 1;
-// Strangerke - Commented (not used)
-// } else {
}
- // label29 :
if (_time > _vm->getOldMilli())
return;
@@ -434,15 +500,15 @@ void Character::update(int32 timeIncrement) {
// skipped all this part.
//label78
+
+
#if 0
if (_id == 0)
- debugC(0, 0xfff, " drew animation flag %d / frame %d", _animFlags, nextFrame);
-
+ debug(" drew animation name %s / flag %d / frame %d", _specialAnim->_name, _animFlags, nextFrame);
if (_id == 1)
- debugC(0, 0xfff, " flux animation flag %d / frame %d", _animFlags, nextFrame);
-
+ debug(" flux animation flag %d / frame %d", _animFlags, nextFrame);
if (_id == 7)
- debugC(0, 0xfff, " footman animation flag %d / frame %d", _animFlags, nextFrame);
+ debug(" footman animation flag %d / frame %d", _animFlags, nextFrame);
#endif
_time = nextTime;
@@ -913,10 +979,12 @@ const SpecialCharacterAnimation *Character::getSpecialAnimation(int32 characterI
bool Character::loadShadowAnimation(Common::String animName) {
debugC(1, kDebugCharacter, "loadShadowAnimation(%s)", animName.c_str());
+ delete _shadowAnim;
_shadowAnim = new Animation(_vm);
if (!_shadowAnim->loadAnimation(animName))
return false;
+ delete _shadowAnimationInstance;
_shadowAnimationInstance = _vm->getAnimationManager()->createNewInstance(kAnimationCharacter);
_vm->getAnimationManager()->addInstance(_shadowAnimationInstance);
_shadowAnimationInstance->setAnimation(_shadowAnim);
@@ -949,11 +1017,8 @@ void Character::playAnim(int32 animId, int32 unused, int32 flags) {
strcat(animName, ".CAF");
- if (_animScriptId != -1)
- _vm->getSceneAnimationScript(_animScriptId)->_frozen = true;
-
- if (_sceneAnimationId > -1)
- setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance);
+ if (_animScriptId != -1 && (flags & 8) == 0)
+ _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true;
stopSpecialAnim();
@@ -963,11 +1028,21 @@ void Character::playAnim(int32 animId, int32 unused, int32 flags) {
// make the talker busy
_flags |= 1;
+
+ // wait for the character to be ready
+ while (_animScriptId != -1 && _animationInstance->getFrame() > 0 && (_specialAnim && _animationInstance->getAnimation() != _specialAnim)) {
+ _vm->simpleUpdate(false);
+ }
}
+
+
+ if (_sceneAnimationId > -1)
+ setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance);
+
+
_animFlags |= flags;
- if (_specialAnim)
- delete _specialAnim;
+ delete _specialAnim;
_specialAnim = new Animation(_vm);
_specialAnim->loadAnimation(animName);
diff --git a/engines/toon/character.h b/engines/toon/character.h
index 997a401403..1927200b12 100644
--- a/engines/toon/character.h
+++ b/engines/toon/character.h
@@ -53,11 +53,12 @@ struct SpecialCharacterAnimation {
class Character {
public:
Character(ToonEngine *vm);
- virtual ~Character(void);
+ virtual ~Character();
virtual void init();
virtual int32 getId();
virtual void setId(int32 id);
virtual void setFacing(int32 facing);
+ virtual void forceFacing(int32 facing);
virtual int32 getFacing();
virtual void setAnimScript(int32 animScriptId);
virtual void setSceneAnimationId(int32 sceneAnimationId);
@@ -69,6 +70,7 @@ public:
virtual int32 getAnimFlag();
virtual void setAnimFlag(int32 flag);
virtual void setPosition(int32 x, int32 y);
+ virtual void forcePosition(int32 x, int32 y);
virtual int32 getX();
virtual int32 getY();
virtual int32 getFinalX();
@@ -95,6 +97,9 @@ public:
virtual void stopSpecialAnim();
virtual void updateIdle();
virtual int32 getRandomIdleAnim() { return 0; }
+ virtual void updateTimers(int32 relativeAdd);
+ virtual void setTalking(bool talking) { _isTalking = talking; }
+ virtual bool isTalking() { return _isTalking; }
int32 getFacingFromDirection(int32 dx, int32 dy);
static const SpecialCharacterAnimation *getSpecialAnimation(int32 characterId, int32 animationId);
@@ -125,6 +130,7 @@ protected:
int32 _speed;
int32 _lastWalkTime;
int32 _numPixelToWalk;
+ bool _isTalking;
AnimationInstance *_animationInstance;
AnimationInstance *_shadowAnimationInstance;
diff --git a/engines/toon/console.cpp b/engines/toon/console.cpp
new file mode 100644
index 0000000000..db50e28489
--- /dev/null
+++ b/engines/toon/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "toon/console.h"
+#include "toon/toon.h"
+
+namespace Toon {
+
+ToonConsole::ToonConsole(ToonEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+ToonConsole::~ToonConsole() {
+}
+
+void ToonConsole::preEnter() {
+}
+
+void ToonConsole::postEnter() {
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/console.h b/engines/toon/console.h
new file mode 100644
index 0000000000..40b556e633
--- /dev/null
+++ b/engines/toon/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef TOON_CONSOLE_H
+#define TOON_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Toon {
+
+class ToonEngine;
+
+class ToonConsole : public GUI::Debugger {
+public:
+ ToonConsole(ToonEngine *vm);
+ virtual ~ToonConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ ToonEngine *_vm;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
index a0017f2571..f8c4c08ce6 100644
--- a/engines/toon/detection.cpp
+++ b/engines/toon/detection.cpp
@@ -112,13 +112,21 @@ static const char * const directoryGlobs[] = {
};
static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
(const byte *)Toon::gameDescriptions,
+ // Size of that superset structure
sizeof(ADGameDescription),
- 5000, // number of md5 bytes
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
ToonGames,
- 0, // no obsolete targets data
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
"toon",
+ // List of files for file-based fallback detection (optional)
Toon::fileBasedFallback,
+ // Flags
0,
// Additional GUI options (for every game}
Common::GUIO_NONE,
@@ -145,19 +153,24 @@ public:
virtual int getMaximumSaveSlot() const;
virtual SaveStateList listSaves(const char *target) const;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
-// virtual void removeSaveState(const char *target, int slot) const;
+ virtual void removeSaveState(const char *target, int slot) const;
};
bool ToonMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
-// (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate);
}
+void ToonMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
int ToonMetaEngine::getMaximumSaveSlot() const { return 99; }
SaveStateList ToonMetaEngine::listSaves(const char *target) const {
@@ -204,7 +217,7 @@ SaveStateList ToonMetaEngine::listSaves(const char *target) const {
}
SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
- Common::String fileName = Common::String::printf("%s.%03d", target, slot);
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
if (file) {
diff --git a/engines/toon/drew.cpp b/engines/toon/drew.cpp
index b50a8db3dc..1d4df09027 100644
--- a/engines/toon/drew.cpp
+++ b/engines/toon/drew.cpp
@@ -35,7 +35,7 @@ CharacterDrew::CharacterDrew(ToonEngine *vm) : Character(vm) {
vm->getAnimationManager()->addInstance(_animationInstance);
}
-CharacterDrew::~CharacterDrew(void) {
+CharacterDrew::~CharacterDrew() {
}
bool CharacterDrew::setupPalette() {
@@ -48,11 +48,6 @@ bool CharacterDrew::setupPalette() {
return false;
}
-void CharacterDrew::setFacing(int32 facing) {
- debugC(4, kDebugCharacter, "setFacing(%d)", facing);
- _facing = facing;
-}
-
void CharacterDrew::setPosition(int32 x, int32 y) {
debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
@@ -88,7 +83,6 @@ void CharacterDrew::playStandingAnim() {
_animationInstance->stopAnimation();
_animationInstance->setLooping(true);
//setVisible(true);
-
}
void CharacterDrew::playWalkAnim(int32 start, int32 end) {
diff --git a/engines/toon/drew.h b/engines/toon/drew.h
index a5be4c76c3..ae9fdff2e9 100644
--- a/engines/toon/drew.h
+++ b/engines/toon/drew.h
@@ -36,9 +36,8 @@ class ToonEngine;
class CharacterDrew : public Character {
public:
CharacterDrew(ToonEngine *vm);
- virtual ~CharacterDrew(void);
+ virtual ~CharacterDrew();
bool setupPalette();
- void setFacing(int32 facing);
void playStandingAnim();
void setPosition(int32 x, int32 y);
void update(int32 timeIncrement);
diff --git a/engines/toon/flux.cpp b/engines/toon/flux.cpp
index 9dd38cd37a..2b5551732b 100644
--- a/engines/toon/flux.cpp
+++ b/engines/toon/flux.cpp
@@ -34,7 +34,7 @@ CharacterFlux::CharacterFlux(ToonEngine *vm) : Character(vm) {
vm->getAnimationManager()->addInstance(_animationInstance);
}
-CharacterFlux::~CharacterFlux(void) {
+CharacterFlux::~CharacterFlux() {
}
void CharacterFlux::playStandingAnim() {
diff --git a/engines/toon/flux.h b/engines/toon/flux.h
index 7981799cba..a90853cb02 100644
--- a/engines/toon/flux.h
+++ b/engines/toon/flux.h
@@ -36,7 +36,7 @@ namespace Toon {
class CharacterFlux : public Character {
public:
CharacterFlux(ToonEngine *vm);
- virtual ~CharacterFlux(void);
+ virtual ~CharacterFlux();
void setPosition(int32 x, int32 y);
void playStandingAnim();
diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp
index be5a306d8c..a8a1091d12 100644
--- a/engines/toon/font.cpp
+++ b/engines/toon/font.cpp
@@ -32,21 +32,39 @@ FontRenderer::FontRenderer(ToonEngine *vm) : _vm(vm) {
_currentFontColor[1] = 0xc8;
_currentFontColor[2] = 0xcb;
_currentFontColor[3] = 0xce;
+}
+FontRenderer::~FontRenderer() {
}
// mapping extended characters required for foreign versions to font (animation)
static const byte map_textToFont[0x80] = {
- '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
- '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
- '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx
- '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xBx
- '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx
- '?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', '?', 0x1f, '?', '?', 0x19, // 0xDx
+ '?', '?', '?', '?', 0x03, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
+ '?', 0x09, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x0a, // 0xBx
+ '?', '?', '?', '?', 0x1d, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx
+ '?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', '?', 0x1f, '?', '?', 0x19, // 0xDx
0x0d, 0x04, 0x0e, '?', 0x1a, '?', '?', 0x18, 0x10, 0x0f, 0x12, 0x11, 0x09, 0x05, 0x14, 0x13, // 0xEx
0x23, 0x08, 0x23, 0x06, 0x15, 0x23, 0x1b, 0x23, 0x23, 0x16, 0x07, 0x17, 0x1c, 0x23, 0x23, 0x23 // 0xFx
};
+byte FontRenderer::textToFont(byte c) {
+ // No need to remap simple characters.
+ if (c < 0x80)
+ return c;
+
+ // The Spanish version shows grave accent over the 'e' when it should
+ // be acute. This happens both in the original interpreter and when
+ // using the common map which works for other languages, so we add a
+ // special case for it.
+ if (_vm->_language == Common::ES_ESP && c == 0xe9)
+ return 0x10;
+
+ // Use the common map to convert the extended characters.
+ return map_textToFont[c - 0x80];
+}
+
void FontRenderer::renderText(int32 x, int32 y, Common::String origText, int32 mode) {
debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
@@ -75,8 +93,7 @@ void FontRenderer::renderText(int32 x, int32 y, Common::String origText, int32 m
height = 0;
curX = x;
} else {
- if (curChar >= 0x80)
- curChar = map_textToFont[curChar - 0x80];
+ curChar = textToFont(curChar);
_currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor);
curX = curX + _currentFont->getFrameWidth(curChar) - 1;
height = MAX(height, _currentFont->getFrameHeight(curChar));
@@ -105,8 +122,7 @@ void FontRenderer::computeSize(Common::String origText, int32 *retX, int32 *retY
lineHeight = 0;
lineWidth = 0;
} else {
- if (curChar >= 0x80)
- curChar = map_textToFont[curChar - 0x80];
+ curChar = textToFont(curChar);
int32 charWidth = _currentFont->getFrameWidth(curChar) - 1;
int32 charHeight = _currentFont->getFrameHeight(curChar);
lineWidth += charWidth;
@@ -192,9 +208,8 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText
if (curChar == 32) {
lastSpace = it;
lastSpaceX = curWidth;
- } else if (curChar >= 0x80) {
- curChar = map_textToFont[curChar - 0x80];
- }
+ } else
+ curChar = textToFont(curChar);
int width = _currentFont->getFrameWidth(curChar);
curWidth += width - 2;
@@ -259,9 +274,7 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText
const byte *line = lines[i];
curX = x - lineSize[i] / 2;
while (*line) {
- byte curChar = *line;
- if (curChar >= 0x80)
- curChar = map_textToFont[curChar - 0x80];
+ byte curChar = textToFont(*line);
if (curChar != 32) _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor);
curX = curX + _currentFont->getFrameWidth(curChar) - 2;
//height = MAX(height, _currentFont->getFrameHeight(curChar));
diff --git a/engines/toon/font.h b/engines/toon/font.h
index 713d8c3409..739d215e36 100644
--- a/engines/toon/font.h
+++ b/engines/toon/font.h
@@ -33,7 +33,7 @@ namespace Toon {
class FontRenderer {
public:
FontRenderer(ToonEngine *vm);
- ~FontRenderer(void);
+ ~FontRenderer();
void setFont(Animation *font);
void computeSize(Common::String origText, int32 *retX, int32 *retY);
@@ -45,6 +45,7 @@ protected:
Animation *_currentFont;
ToonEngine *_vm;
byte _currentFontColor[4];
+ byte textToFont(byte c);
};
} // End of namespace Toon
diff --git a/engines/toon/hotspot.cpp b/engines/toon/hotspot.cpp
index 5af61197d7..687ea6ee83 100644
--- a/engines/toon/hotspot.cpp
+++ b/engines/toon/hotspot.cpp
@@ -29,10 +29,13 @@
namespace Toon {
Hotspots::Hotspots(ToonEngine *vm) : _vm(vm) {
- _items = 0;
+ _items = NULL;
_numItems = 0;
}
+Hotspots::~Hotspots() {
+ delete[] _items;
+}
void Hotspots::load(Common::ReadStream *Stream) {
delete[] _items;
@@ -47,7 +50,6 @@ void Hotspots::load(Common::ReadStream *Stream) {
}
void Hotspots::save(Common::WriteStream *Stream) {
-
Stream->writeSint16BE(_numItems);
for (int32 i = 0; i < _numItems; i++) {
@@ -121,9 +123,7 @@ bool Hotspots::LoadRif(Common::String rifName, Common::String additionalRifName)
_numItems = (rifsize + rifsize2) / 512;
- if (_items)
- delete[] _items;
-
+ delete[] _items;
_items = new HotspotData[_numItems];
// RIFs are compressed in RNC1
diff --git a/engines/toon/hotspot.h b/engines/toon/hotspot.h
index 233bcebcb7..aabcd531fe 100644
--- a/engines/toon/hotspot.h
+++ b/engines/toon/hotspot.h
@@ -51,7 +51,7 @@ private:
class Hotspots {
public:
Hotspots(ToonEngine *vm);
- ~Hotspots(void);
+ ~Hotspots();
bool LoadRif(Common::String rifName, Common::String additionalRifName);
int32 Find(int32 x, int32 y);
diff --git a/engines/toon/module.mk b/engines/toon/module.mk
index 403408e497..7796203d00 100644
--- a/engines/toon/module.mk
+++ b/engines/toon/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
anim.o \
audio.o \
character.o \
+ console.o \
conversation.o \
detection.o \
drew.o \
diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp
index 87a3e878b5..f0c40366be 100644
--- a/engines/toon/movie.cpp
+++ b/engines/toon/movie.cpp
@@ -46,7 +46,10 @@ bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forc
if (forcedflags & 0x10 || _surface->h == 200) {
_header.flags = 4;
- delete this->_surface;
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
_surface = new Graphics::Surface();
_surface->create(640, 400, 1);
}
@@ -56,15 +59,17 @@ bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forc
}
ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Graphics::SmackerDecoder(mixer, soundType) {
-
}
+// decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed
Movie::Movie(ToonEngine *vm , ToonstruckSmackerDecoder *decoder) {
_vm = vm;
+ _playing = false;
_decoder = decoder;
}
Movie::~Movie() {
+ delete _decoder;
}
void Movie::init() const {
@@ -73,14 +78,16 @@ void Movie::init() const {
void Movie::play(Common::String video, int32 flags) {
debugC(1, kDebugMovie, "play(%s, %d)", video.c_str(), flags);
+ _playing = true;
if (flags & 1)
_vm->getAudioManager()->setMusicVolume(0);
_decoder->loadFile(video.c_str(), flags);
playVideo();
_vm->flushPalette();
if (flags & 1)
- _vm->getAudioManager()->setMusicVolume(100);
+ _vm->getAudioManager()->setMusicVolume(_vm->getAudioManager()->isMusicMuted() ? 0 : 255);
_decoder->close();
+ _playing = false;
}
bool Movie::playVideo() {
diff --git a/engines/toon/movie.h b/engines/toon/movie.h
index 8e1acc4a77..e89abb56c0 100644
--- a/engines/toon/movie.h
+++ b/engines/toon/movie.h
@@ -34,6 +34,7 @@ namespace Toon {
class ToonstruckSmackerDecoder : public Graphics::SmackerDecoder {
public:
ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
+ virtual ~ToonstruckSmackerDecoder() {}
void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
bool loadFile(const Common::String &filename, int forcedflags) ;
};
@@ -41,16 +42,19 @@ public:
class Movie {
public:
Movie(ToonEngine *vm, ToonstruckSmackerDecoder *decoder);
- ~Movie(void);
+ virtual ~Movie(void);
void init() const;
void play(Common::String video, int32 flags = 0);
+ bool isPlaying() { return _playing; }
protected:
bool playVideo();
ToonEngine *_vm;
Audio::Mixer *_mixer;
ToonstruckSmackerDecoder *_decoder;
+ bool _playing;
+
};
} // End of namespace Toon
diff --git a/engines/toon/path.cpp b/engines/toon/path.cpp
index 484e621a64..1d3b32b804 100644
--- a/engines/toon/path.cpp
+++ b/engines/toon/path.cpp
@@ -27,9 +27,20 @@
namespace Toon {
+PathFindingHeap::PathFindingHeap() {
+ _count = 0;
+ _alloc = 0;
+ _data = NULL;
+}
+
+PathFindingHeap::~PathFindingHeap() {
+ delete[] _data;
+}
+
int32 PathFindingHeap::init(int32 size) {
debugC(1, kDebugPath, "init(%d)", size);
+ delete[] _data;
_data = new HeapDataGrid[size * 2];
memset(_data, 0, sizeof(HeapDataGrid) * size * 2);
_count = 0;
@@ -37,13 +48,13 @@ int32 PathFindingHeap::init(int32 size) {
return size;
}
-int PathFindingHeap::unload() {
- if (_data)
- delete[] _data;
+int32 PathFindingHeap::unload() {
+ delete[] _data;
+ _data = NULL;
return 0;
}
-int PathFindingHeap::clear() {
+int32 PathFindingHeap::clear() {
//debugC(1, kDebugPath, "clear()");
_count = 0;
@@ -51,7 +62,7 @@ int PathFindingHeap::clear() {
return 1;
}
-int PathFindingHeap::push(int x, int y, int weight) {
+int32 PathFindingHeap::push(int32 x, int32 y, int32 weight) {
//debugC(6, kDebugPath, "push(%d, %d, %d)", x, y, weight);
_count++;
@@ -126,15 +137,15 @@ PathFinding::PathFinding(ToonEngine *vm) : _vm(vm) {
_width = 0;
_height = 0;
_heap = new PathFindingHeap();
- _gridTemp = 0;
+ _gridTemp = NULL;
_numBlockingRects = 0;
}
PathFinding::~PathFinding(void) {
- if (_heap) {
+ if (_heap)
_heap->unload();
- delete _heap;
- }
+ delete _heap;
+ delete[] _gridTemp;
}
bool PathFinding::isWalkable(int32 x, int32 y) {
@@ -193,7 +204,34 @@ int32 PathFinding::findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32
}
}
-int PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) {
+bool PathFinding::lineIsWalkable(int32 x, int32 y, int32 x2, int32 y2) {
+
+ uint32 bx = x << 16;
+ int32 dx = x2 - x;
+ uint32 by = y << 16;
+ int32 dy = y2 - y;
+ uint32 adx = abs(dx);
+ uint32 ady = abs(dy);
+ int32 t = 0;
+ if (adx <= ady)
+ t = ady;
+ else
+ t = adx;
+
+ int32 cdx = (dx << 16) / t;
+ int32 cdy = (dy << 16) / t;
+
+ int32 i = t;
+ while (i) {
+ if(!isWalkable(bx >> 16, by >> 16))
+ return false;
+ bx += cdx;
+ by += cdy;
+ i--;
+ }
+ return true;
+}
+int32 PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) {
debugC(1, kDebugPath, "findPath(%d, %d, %d, %d)", x, y, destx, desty);
if (x == destx && y == desty) {
@@ -201,6 +239,9 @@ int PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) {
return true;
}
+ // first test direct line
+ //if(lineIsWalkable(x,y,destx,desty))
+
memset(_gridTemp , 0, _width * _height * sizeof(int32));
_heap->clear();
int32 curX = x;
@@ -212,18 +253,15 @@ int PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) {
_heap->push(curX, curY, abs(destx - x) + abs(desty - y));
int wei = 0;
-// Strangerke - Commented (not used)
-// byte *mask = _currentMask->getDataPtr();
-
while (_heap->_count) {
wei = 0;
_heap->pop(&curX, &curY, &curWeight);
int curNode = curX + curY * _width;
- int32 endX = MIN(curX + 1, _width - 1);
- int32 endY = MIN(curY + 1, _height - 1);
- int32 startX = MAX(curX - 1, 0);
- int32 startY = MAX(curY - 1, 0);
+ int32 endX = MIN<int32>(curX + 1, _width - 1);
+ int32 endY = MIN<int32>(curY + 1, _height - 1);
+ int32 startX = MAX<int32>(curX - 1, 0);
+ int32 startY = MAX<int32>(curY - 1, 0);
for (int32 px = startX; px <= endX; px++) {
for (int py = startY; py <= endY; py++) {
@@ -271,10 +309,10 @@ next:
int32 bestX = -1;
int32 bestY = -1;
- int32 endX = MIN(curX + 1, _width - 1);
- int32 endY = MIN(curY + 1, _height - 1);
- int32 startX = MAX(curX - 1, 0);
- int32 startY = MAX(curY - 1, 0);
+ int32 endX = MIN<int32>(curX + 1, _width - 1);
+ int32 endY = MIN<int32>(curY + 1, _height - 1);
+ int32 startX = MAX<int32>(curX - 1, 0);
+ int32 startY = MAX<int32>(curY - 1, 0);
for (int32 px = startX; px <= endX; px++) {
for (int32 py = startY; py <= endY; py++) {
@@ -323,8 +361,7 @@ void PathFinding::init(Picture *mask) {
_currentMask = mask;
_heap->unload();
_heap->init(_width * _height);
- if (_gridTemp)
- delete[] _gridTemp;
+ delete[] _gridTemp;
_gridTemp = new int32[_width*_height];
}
diff --git a/engines/toon/path.h b/engines/toon/path.h
index d8ef2eac02..507c4dd2c2 100644
--- a/engines/toon/path.h
+++ b/engines/toon/path.h
@@ -37,28 +37,32 @@ struct HeapDataGrid {
};
class PathFindingHeap {
-
-private:
- HeapDataGrid *_data;
public:
+ PathFindingHeap();
+ ~PathFindingHeap();
+
int32 _alloc;
int32 _count;
+
int32 push(int32 x, int32 y, int32 weight);
int32 pop(int32 *x, int32 *y, int32 *weight);
int32 init(int32 size);
int32 clear();
int32 unload();
-};
+private:
+ HeapDataGrid *_data;
+};
class PathFinding {
public:
PathFinding(ToonEngine *vm);
- ~PathFinding(void);
+ ~PathFinding();
int32 findPath(int32 x, int32 y, int32 destX, int32 destY);
int32 findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32 *fyy, int origX = -1, int origY = -1);
bool isWalkable(int32 x, int32 y);
+ bool lineIsWalkable(int32 x, int32 y, int32 x2, int32 y2);
void init(Picture *mask);
void resetBlockingRects();
diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp
index 11a5572066..b797079c00 100644
--- a/engines/toon/picture.cpp
+++ b/engines/toon/picture.cpp
@@ -130,7 +130,13 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) {
}
Picture::Picture(ToonEngine *vm) : _vm(vm) {
+ _data = NULL;
+ _palette = NULL;
+}
+Picture::~Picture() {
+ delete[] _data;
+ delete[] _palette;
}
void Picture::setupPalette() {
diff --git a/engines/toon/picture.h b/engines/toon/picture.h
index 5065843b3c..1b0fd7f550 100644
--- a/engines/toon/picture.h
+++ b/engines/toon/picture.h
@@ -40,6 +40,7 @@ class Picture {
public:
Picture(ToonEngine *vm);
+ ~Picture();
bool loadPicture(Common::String file, bool totalPalette = false);
void setupPalette();
void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy);
diff --git a/engines/toon/resource.cpp b/engines/toon/resource.cpp
index 348aa45ae9..61e3ffb111 100644
--- a/engines/toon/resource.cpp
+++ b/engines/toon/resource.cpp
@@ -25,11 +25,26 @@
#include "toon/resource.h"
#include "common/file.h"
+#include "common/memstream.h"
+#include "common/substream.h"
#include "toon/toon.h"
namespace Toon {
+Resources::Resources(ToonEngine *vm) : _vm(vm) {
+}
+
+Resources::~Resources() {
+ while(!_pakFiles.empty()) {
+ PakFile *temp = _pakFiles.back();
+ _pakFiles.pop_back();
+ delete temp;
+ }
+
+ purgeFileData();
+}
+
void Resources::openPackage(Common::String fileName, bool preloadEntirePackage) {
debugC(1, kDebugResource, "openPackage(%s, %d)", fileName.c_str(), (preloadEntirePackage) ? 1 : 0);
@@ -39,7 +54,6 @@ void Resources::openPackage(Common::String fileName, bool preloadEntirePackage)
if (!opened)
return;
-
PakFile *pakFile = new PakFile();
pakFile->open(&file, fileName, preloadEntirePackage);
@@ -59,10 +73,6 @@ void Resources::closePackage(Common::String fileName) {
}
}
-Resources::Resources(ToonEngine *vm) : _vm(vm) {
-
-}
-
uint8 *Resources::getFileData(Common::String fileName, uint32 *fileSize) {
debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.c_str());
@@ -78,6 +88,7 @@ uint8 *Resources::getFileData(Common::String fileName, uint32 *fileSize) {
uint8 *memory = (uint8 *)new uint8[*fileSize];
file.read(memory, *fileSize);
file.close();
+ _allocatedFileData.push_back(memory);
return memory;
} else {
for (uint32 i = 0; i < _pakFiles.size(); i++) {
@@ -118,6 +129,13 @@ Common::SeekableReadStream *Resources::openFile(Common::String fileName) {
return 0;
}
}
+
+void Resources::purgeFileData() {
+ for (uint32 i = 0; i < _allocatedFileData.size(); i++) {
+ delete[] _allocatedFileData[i];
+ }
+ _allocatedFileData.clear();
+}
Common::SeekableReadStream *PakFile::createReadStream(Common::String fileName) {
debugC(1, kDebugResource, "createReadStream(%s)", fileName.c_str());
@@ -184,6 +202,7 @@ void PakFile::open(Common::SeekableReadStream *rs, Common::String packName, bool
if (preloadEntirePackage) {
_bufferSize = rs->size();
+ delete[] _buffer;
_buffer = new uint8[_bufferSize];
rs->seek(0);
rs->read(_buffer, _bufferSize);
@@ -191,9 +210,7 @@ void PakFile::open(Common::SeekableReadStream *rs, Common::String packName, bool
}
void PakFile::close() {
- if (_buffer) {
- delete[] _buffer;
- }
+ delete[] _buffer;
if (_fileHandle) {
_fileHandle->close();
@@ -201,15 +218,15 @@ void PakFile::close() {
}
}
-PakFile::~PakFile() {
- close();
-}
-
-
PakFile::PakFile() {
- _fileHandle = 0;
- _buffer = 0;
_bufferSize = 0;
+ _buffer = NULL;
+
+ _fileHandle = NULL;
+}
+
+PakFile::~PakFile() {
+ close();
}
} // End of namespace Toon
diff --git a/engines/toon/resource.h b/engines/toon/resource.h
index 3a080fe894..e117c8e259 100644
--- a/engines/toon/resource.h
+++ b/engines/toon/resource.h
@@ -58,8 +58,6 @@ protected:
uint32 _numFiles;
Common::Array<File> _files;
Common::File *_fileHandle;
-
-
};
class ToonEngine;
@@ -67,15 +65,17 @@ class ToonEngine;
class Resources {
public:
Resources(ToonEngine *vm);
+ ~Resources();
void openPackage(Common::String file, bool preloadEntirePackage);
void closePackage(Common::String fileName);
Common::SeekableReadStream *openFile(Common::String file);
- uint8 *getFileData(Common::String fileName, uint32 *fileSize);
+ uint8 *getFileData(Common::String fileName, uint32 *fileSize); // this memory must be copied to your own structures!
+ void purgeFileData();
protected:
ToonEngine *_vm;
+ Common::Array<uint8 *> _allocatedFileData;
Common::Array<PakFile *> _pakFiles;
-
};
} // End of namespace Toon
diff --git a/engines/toon/script.cpp b/engines/toon/script.cpp
index 06d482f4e2..3cd56761f6 100644
--- a/engines/toon/script.cpp
+++ b/engines/toon/script.cpp
@@ -66,9 +66,13 @@ EMCInterpreter::EMCInterpreter(ToonEngine *vm) : _vm(vm), _scriptData(0), _filen
#undef OPCODE
}
+EMCInterpreter::~EMCInterpreter() {
+}
+
bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
switch (chunk._type) {
case MKID_BE('TEXT'):
+ delete[] _scriptData->text;
_scriptData->text = new byte[chunk._size];
assert(_scriptData->text);
if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size)
@@ -76,6 +80,7 @@ bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
break;
case MKID_BE('ORDR'):
+ delete[] _scriptData->ordr;
_scriptData->ordr = new uint16[chunk._size >> 1];
assert(_scriptData->ordr);
if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size)
@@ -86,6 +91,7 @@ bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
break;
case MKID_BE('DATA'):
+ delete[] _scriptData->data;
_scriptData->data = new uint16[chunk._size >> 1];
assert(_scriptData->data);
if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size)
@@ -102,7 +108,7 @@ bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
return false;
}
-bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const Opcode *> *opcodes) {
+bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const OpcodeV2 *> *opcodes) {
Common::SeekableReadStream *stream = _vm->resources()->openFile(filename);
if (!stream) {
error("Couldn't open script file '%s'", filename);
@@ -145,11 +151,13 @@ void EMCInterpreter::unload(EMCData *data) {
return;
delete[] data->text;
+ data->text = NULL;
+
delete[] data->ordr;
- delete[] data->data;
+ data->ordr = NULL;
- data->text = 0;
- data->ordr = data->data = 0;
+ delete[] data->data;
+ data->data = NULL;
}
void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) {
@@ -181,7 +189,6 @@ bool EMCInterpreter::isValid(EMCState *script) {
}
bool EMCInterpreter::run(EMCState *script) {
-
if (script->running)
return false;
@@ -192,7 +199,6 @@ bool EMCInterpreter::run(EMCState *script) {
script->running = true;
-
// Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original
// would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway.
const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data);
@@ -216,7 +222,7 @@ bool EMCInterpreter::run(EMCState *script) {
static bool EMCDebug = false;
if (EMCDebug)
debugC(5, 0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset * 2, _opcodes[opcode].desc, _parameter, (uint)_parameter);
- //debug(0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter);
+ //printf( "[0x%.08X] EMCInterpreter::%s([%d/%u])\n", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter);
(this->*(_opcodes[opcode].proc))(script);
}
diff --git a/engines/toon/script.h b/engines/toon/script.h
index 47e04e8c82..6c46238238 100644
--- a/engines/toon/script.h
+++ b/engines/toon/script.h
@@ -36,7 +36,8 @@
namespace Toon {
struct EMCState;
-typedef Common::Functor1<EMCState *, int> Opcode;
+class ScriptFunc;
+typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
struct EMCData {
char filename[13];
@@ -46,7 +47,7 @@ struct EMCData {
uint16 *ordr;
uint16 dataSize;
- const Common::Array<const Opcode *> *sysFuncs;
+ const Common::Array<const OpcodeV2 *> *sysFuncs;
};
struct EMCState {
@@ -97,8 +98,9 @@ public:
class EMCInterpreter {
public:
EMCInterpreter(ToonEngine *vm);
+ ~EMCInterpreter();
- bool load(const char *filename, EMCData *data, const Common::Array<const Opcode *> *opcodes);
+ bool load(const char *filename, EMCData *data, const Common::Array<const OpcodeV2 *> *opcodes);
void unload(EMCData *data);
void init(EMCState *scriptState, const EMCData *data);
@@ -146,6 +148,7 @@ private:
void op_eval(EMCState *);
void op_setRetAndJmp(EMCState *);
};
+
} // End of namespace Toon
#endif
diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp
index 821a8971de..e2c9540fb2 100644
--- a/engines/toon/script_func.cpp
+++ b/engines/toon/script_func.cpp
@@ -34,13 +34,12 @@
namespace Toon {
-typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
#define SetOpcodeTable(x) table = &x;
#define Opcode(x) table->push_back(new OpcodeV2(this, &ScriptFunc::x))
#define OpcodeUnImpl() table->push_back(new OpcodeV2(this, 0))
ScriptFunc::ScriptFunc(ToonEngine *vm) {
- Common::Array<const Opcode *> *table = 0;
+ Common::Array<const OpcodeV2 *> *table = 0;
_vm = vm;
_opcodes.reserve(176);
@@ -224,7 +223,11 @@ ScriptFunc::ScriptFunc(ToonEngine *vm) {
}
ScriptFunc::~ScriptFunc(void) {
-
+ while(!_opcodes.empty()) {
+ const OpcodeV2 *temp = _opcodes.back();
+ _opcodes.pop_back();
+ delete temp;
+ }
}
char *GetText(int32 i, EMCState *state) {
@@ -239,7 +242,7 @@ int32 ScriptFunc::sys_Cmd_Dummy(EMCState *state) {
}
int32 ScriptFunc::sys_Cmd_Change_Actor_X_And_Y(EMCState *state) {
- _vm->getDrew()->setPosition(stackPos(0), stackPos(1));
+ _vm->getDrew()->forcePosition(stackPos(0), stackPos(1));
return 0;
}
@@ -344,7 +347,7 @@ int32 ScriptFunc::sys_Cmd_Set_Sack_Visible(EMCState *state) {
}
int32 ScriptFunc::sys_Cmd_Set_Actor_Facing(EMCState *state) {
- _vm->getDrew()->setFacing(stackPos(0));
+ _vm->getDrew()->forceFacing(stackPos(0));
_vm->getDrew()->playStandingAnim();
return 0;
}
@@ -474,6 +477,14 @@ int32 ScriptFunc::sys_Cmd_Empty_Inventory(EMCState *state) {
}
int32 ScriptFunc::sys_Cmd_Set_Anim_Scale_Size(EMCState *state) {
+ int32 animID = stackPos(0);
+ int32 scale = stackPos(1);
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animID);
+ if (sceneAnim) {
+ sceneAnim->_animInstance->setUseMask(true);
+ sceneAnim->_animInstance->setScale(scale,true);
+ }
return 0;
}
int32 ScriptFunc::sys_Cmd_Delete_Item_From_Inventory(EMCState *state) {
@@ -541,7 +552,11 @@ int32 ScriptFunc::sys_Cmd_Exit_Conversation(EMCState *state) {
}
int32 ScriptFunc::sys_Cmd_Set_Mouse_Pos(EMCState *state) {
- _vm->getSystem()->warpMouse(stackPos(0) - _vm->state()->_currentScrollValue, stackPos(1));
+ if (_vm->state()->_inCloseUp) {
+ _vm->getSystem()->warpMouse(stackPos(0), stackPos(1));
+ } else {
+ _vm->getSystem()->warpMouse(stackPos(0) - _vm->state()->_currentScrollValue, stackPos(1));
+ }
return 0;
}
@@ -621,7 +636,7 @@ int32 ScriptFunc::sys_Cmd_Character_Talking(EMCState *state) {
int32 characterId = stackPos(0);
Character *character = _vm->getCharacterById(characterId);
if (character)
- return (character->getFlag() & 4) && (character->getFlag() & 8);
+ return character->isTalking();
return 0;
}
@@ -634,14 +649,14 @@ int32 ScriptFunc::sys_Cmd_Set_Flux_Facing_Point(EMCState *state) {
}
int32 ScriptFunc::sys_Cmd_Set_Flux_Facing(EMCState *state) {
- _vm->getFlux()->setFacing(stackPos(0));
+ _vm->getFlux()->forceFacing(stackPos(0));
_vm->getFlux()->playStandingAnim();
return 0;
}
int32 ScriptFunc::sys_Cmd_Set_Flux_Coords(EMCState *state) {
_vm->getFlux()->stopWalk();
- _vm->getFlux()->setPosition(stackPos(0), stackPos(1));
+ _vm->getFlux()->forcePosition(stackPos(0), stackPos(1));
return 0;
}
@@ -702,7 +717,7 @@ int32 ScriptFunc::sys_Cmd_Place_Scene_Anim(EMCState *state) {
int32 frame = stackPos(5);
SceneAnimation *sceneAnim = _vm->getSceneAnimation(sceneId);
- sceneAnim->_animInstance->setPosition(x, y, 0, false);
+ sceneAnim->_animInstance->setPosition(x, y, sceneAnim->_animInstance->getZ(), false);
sceneAnim->_animInstance->forceFrame(frame);
_vm->setSceneAnimationScriptUpdate(false);
return 0;
@@ -874,6 +889,12 @@ int32 ScriptFunc::sys_Cmd_Set_Scene_Anim_Wait(EMCState *state) {
_vm->setSceneAnimationScriptUpdate(false);
}
+ // WORKAROUND : Slow down just a little the guards dance animation so that the voices don't cut
+ if (_vm->state()->_currentScene == 2 && (sceneId == 2 || sceneId == 3)) {
+ waitTicks = 7;
+ _vm->setSceneAnimationScriptUpdate(false);
+ }
+
waitTicks *= _vm->getTickLength();
if (sceneId >= 0 && sceneId < 40) {
@@ -895,9 +916,13 @@ int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) {
if (sceneAnim->_active)
return 0;
+ delete sceneAnim->_animation;
+ delete sceneAnim->_animInstance;
+
sceneAnim->_animation = new Animation(_vm);
sceneAnim->_animation->loadAnimation(GetText(12, state));
sceneAnim->_animInstance = _vm->getAnimationManager()->createNewInstance(kAnimationScene);
+ sceneAnim->_originalAnimInstance = sceneAnim->_animInstance;
sceneAnim->_animInstance->setAnimation(sceneAnim->_animation);
sceneAnim->_animInstance->setVisible((flags & 1) != 0);
sceneAnim->_animInstance->setAnimationRange(stackPos(11), stackPos(11));
@@ -910,10 +935,14 @@ int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) {
int32 dx = stackPos(4);
int32 dy = stackPos(5);
+ int32 x = stackPos(2);
int32 layerZ = stackPos(3);
if (dx == -2)
sceneAnim->_animInstance->moveRelative(640, 0, 0);
+ else if (dx < 0) {
+ dx = sceneAnim->_animation->_x1;
+ }
else if (dx >= 0)
sceneAnim->_animInstance->setX(dx);
@@ -922,8 +951,10 @@ int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) {
else
dy = sceneAnim->_animation->_y1;
- if (flags & 0x20)
- sceneAnim->_animInstance->setZ(_vm->getLayerAtPoint(dx, dy));
+ if (flags & 0x20) {
+ sceneAnim->_animInstance->setZ(_vm->getLayerAtPoint(x, layerZ));
+ sceneAnim->_animInstance->setUseMask(true);
+ }
if (layerZ >= 0) {
sceneAnim->_animInstance->setLayerZ(layerZ);
@@ -961,6 +992,7 @@ int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame(EMCState *state) {
SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId);
if (sceneAnim->_active) {
+ sceneAnim->_animInstance->setAnimation(sceneAnim->_animation);
sceneAnim->_animInstance->setFrame(frame);
sceneAnim->_animInstance->setAnimationRange(frame, frame);
sceneAnim->_animInstance->stopAnimation();
@@ -1048,18 +1080,26 @@ int32 ScriptFunc::sys_Cmd_Play_Sfx(EMCState *state) {
}
int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx(EMCState *state) {
+ //debug("Ambient Sfx : %d %d %d %d", stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _vm->getAudioManager()->startAmbientSFX(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
return 0;
}
int32 ScriptFunc::sys_Cmd_Kill_Ambient_Sfx(EMCState *state) {
+ //debug("Kill Sfx : %d", stackPos(0));
+ _vm->getAudioManager()->killAmbientSFX(stackPos(0));
return 0;
}
int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx_Plus(EMCState *state) {
+ //debug("Ambient Sfx Plus: %d %d %d %d %d %d %d %d", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7));
+ _vm->getAudioManager()->startAmbientSFX(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
return 0;
}
int32 ScriptFunc::sys_Cmd_Set_Ambient_Volume(EMCState *state) {
+ //debug("Ambient Volume : %d %d", stackPos(0), stackPos(1));
+ _vm->getAudioManager()->setAmbientSFXVolume(stackPos(0), stackPos(1));
return 0;
}
@@ -1119,8 +1159,18 @@ int32 ScriptFunc::sys_Cmd_Remove_Scene_Anim(EMCState *state) {
SceneAnimation *sceneAnim = _vm->getSceneAnimation(sceneId);
sceneAnim->_active = false;
_vm->getAnimationManager()->removeInstance(sceneAnim->_animInstance);
- sceneAnim->_animation = 0;
- sceneAnim->_animInstance = 0;
+ delete sceneAnim->_animation;
+ sceneAnim->_animation = NULL;
+
+ // see if one character shares this instance
+ for (int32 c = 0; c < 32; c++) {
+ if (_vm->getCharacter(c) && _vm->getCharacter(c)->getAnimationInstance() == sceneAnim->_originalAnimInstance) {
+ _vm->getCharacter(c)->setAnimationInstance(NULL);
+ }
+ }
+ delete sceneAnim->_originalAnimInstance;
+ sceneAnim->_originalAnimInstance = NULL;
+ sceneAnim->_animInstance = NULL;
return 0;
}
diff --git a/engines/toon/script_func.h b/engines/toon/script_func.h
index 2f8972134c..76b7b0ada1 100644
--- a/engines/toon/script_func.h
+++ b/engines/toon/script_func.h
@@ -31,12 +31,15 @@
namespace Toon {
+class ScriptFunc;
+
+typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
class ScriptFunc {
public:
ScriptFunc(ToonEngine *vm);
~ScriptFunc(void);
- Common::Array<const Opcode *> _opcodes;
+ Common::Array<const OpcodeV2 *> _opcodes;
ToonEngine *_vm;
#define SYSFUNC(x) int32 x(EMCState*)
diff --git a/engines/toon/text.cpp b/engines/toon/text.cpp
index c18e0cbdc8..c54ea87d50 100644
--- a/engines/toon/text.cpp
+++ b/engines/toon/text.cpp
@@ -27,14 +27,13 @@
namespace Toon {
-
TextResource::TextResource(ToonEngine *vm) : _vm(vm) {
_numTexts = 0;
- _textData = 0;
+ _textData = NULL;
}
TextResource::~TextResource(void) {
-
+ delete[] _textData;
}
bool TextResource::loadTextResource(Common::String fileName) {
@@ -45,6 +44,7 @@ bool TextResource::loadTextResource(Common::String fileName) {
if (!data)
return false;
+ delete[] _textData;
_textData = new uint8[fileSize];
memcpy(_textData, data, fileSize);
_numTexts = READ_LE_UINT16(data);
diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp
index 7489f5c5d9..61b6e1bfe3 100644
--- a/engines/toon/toon.cpp
+++ b/engines/toon/toon.cpp
@@ -29,6 +29,9 @@
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/EventRecorder.h"
+#include "common/savefile.h"
+#include "common/memstream.h"
+
#include "engines/util.h"
#include "graphics/surface.h"
#include "graphics/thumbnail.h"
@@ -105,7 +108,7 @@ void ToonEngine::init() {
resources()->openPackage("misc/drew.pak", true);
for (int32 i = 0; i < 32; i++)
- _characters[i] = 0;
+ _characters[i] = NULL;
_characters[0] = new CharacterDrew(this);
_characters[1] = new CharacterFlux(this);
@@ -131,7 +134,6 @@ void ToonEngine::init() {
memset(_sceneAnimations, 0, sizeof(_sceneAnimations));
memset(_sceneAnimationScripts, 0, sizeof(_sceneAnimationScripts));
-
_gameState->_currentChapter = 1;
initChapter();
loadCursor();
@@ -161,7 +163,7 @@ void ToonEngine::waitForScriptStep() {
// Wait after a specified number of script steps when executing a script
// to lower CPU usage
if (++_scriptStep >= 40) {
- g_system->delayMillis(10);
+ g_system->delayMillis(1);
_scriptStep = 0;
}
}
@@ -182,16 +184,30 @@ void ToonEngine::parseInput() {
_audioManager->stopCurrentVoice();
}
if (event.kbd.keycode == Common::KEYCODE_F5) {
- saveGame(-1);
+ if(canSaveGameStateCurrently())
+ saveGame(-1, Common::String());
}
if (event.kbd.keycode == Common::KEYCODE_F6) {
- loadGame(-1);
+ if(canLoadGameStateCurrently())
+ loadGame(-1);
+ }
+ if (event.kbd.ascii == 't') {
+ _showConversationText = !_showConversationText;
+ }
+ if (event.kbd.ascii == 'm') {
+ _audioManager->muteMusic(!_audioManager->isMusicMuted());
+ }
+ if (event.kbd.ascii == 'd') {
+ _audioManager->muteVoice(!_audioManager->isVoiceMuted());
+ }
+ if (event.kbd.ascii == 's') {
+ _audioManager->muteSfx(!_audioManager->isSfxMuted());
}
if (event.kbd.flags & Common::KBD_ALT) {
int32 slotNum = event.kbd.ascii - '0';
- if (slotNum >= 0 && slotNum <= 9) {
- if (saveGame(slotNum)) {
+ if (slotNum >= 0 && slotNum <= 9 && canSaveGameStateCurrently()) {
+ if (saveGame(slotNum, Common::String())) {
// ok
char buf[256];
snprintf(buf, 256, "Saved game in slot #%d ", slotNum);
@@ -210,7 +226,7 @@ void ToonEngine::parseInput() {
if (event.kbd.flags & Common::KBD_CTRL) {
int32 slotNum = event.kbd.ascii - '0';
- if (slotNum >= 0 && slotNum <= 9) {
+ if (slotNum >= 0 && slotNum <= 9 && canLoadGameStateCurrently()) {
if (loadGame(slotNum)) {
// ok
char buf[256];
@@ -225,6 +241,11 @@ void ToonEngine::parseInput() {
dialog.runModal();
}
}
+
+ if (event.kbd.keycode == Common::KEYCODE_d) {
+ _console->attach();
+ _console->onFrame();
+ }
}
break;
// Strangerke - Commented (not used)
@@ -345,6 +366,7 @@ void ToonEngine::update(int32 timeIncrement) {
updateTimer(timeIncrement);
updateTimers();
updateScrolling(false, timeIncrement);
+ _audioManager->updateAmbientSFX();
_animationManager->update(timeIncrement);
_cursorAnimationInstance->update(timeIncrement);
@@ -400,9 +422,58 @@ void ToonEngine::render() {
}
}
+void ToonEngine::doMagnifierEffect() {
+ int32 posX = _mouseX + state()->_currentScrollValue - _cursorOffsetX;
+ int32 posY = _mouseY - _cursorOffsetY - 2;
+
+ Graphics::Surface &surface = *_mainSurface;
+
+ // fast sqrt table lookup (values up to 144 only)
+ static const byte intSqrt[] = {
+ 0, 1, 1, 1, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 12
+ };
+
+ byte tempBuffer[25*25];
+ for (int32 y = -12; y <= 12; y++) {
+ for (int32 x = -12; x <= 12; x++) {
+ int32 destPitch = surface.pitch;
+ uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x);
+ tempBuffer[(y+12) * 25 + x + 12] = *curRow;
+ }
+ }
+
+ for (int32 y = -12; y <= 12; y++) {
+ for (int32 x = -12; x <= 12; x++) {
+ int32 dist = y * y + x * x;
+ if (dist > 144)
+ continue;
+ int32 destPitch = surface.pitch;
+ uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x);
+ int32 lerp = (512 + intSqrt[dist] * 256 / 12);
+ *curRow = tempBuffer[(y*lerp/1024+12) * 25 + x*lerp/1024 + 12];
+ }
+ }
+}
+
void ToonEngine::copyToVirtualScreen(bool updateScreen) {
// render cursor last
if (!_gameState->_mouseHidden) {
+ if (_cursorAnimationInstance->getFrame() == 7) // magnifier icon needs a special effect
+ doMagnifierEffect();
_cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false);
_cursorAnimationInstance->render();
}
@@ -421,8 +492,6 @@ void ToonEngine::doFrame() {
render();
int32 currentTimer = _system->getMillis();
-// Strangerke - Commented (not used)
-// int32 elapsedTime = currentTimer - _oldTimer;
update(currentTimer - _oldTimer);
_oldTimer = currentTimer;
@@ -507,6 +576,7 @@ bool ToonEngine::showMainmenu(bool &loadedGame) {
bool exitGame = false;
int clickingOn, clickRelease;
int menuMask = MAINMENUMASK_BASE;
+ Common::SeekableReadStream *mainmenuMusicFile = NULL;
AudioStreamInstance *mainmenuMusic = NULL;
bool musicPlaying = false;
@@ -515,7 +585,7 @@ bool ToonEngine::showMainmenu(bool &loadedGame) {
clickRelease = false;
if (!musicPlaying) {
- Common::SeekableReadStream *mainmenuMusicFile = resources()->openFile("misc/BR091013.MUS");
+ mainmenuMusicFile = resources()->openFile("misc/BR091013.MUS");
mainmenuMusic = new AudioStreamInstance(_audioManager, _mixer, mainmenuMusicFile, true);
mainmenuMusic->play(false);
musicPlaying = true;
@@ -585,11 +655,10 @@ bool ToonEngine::showMainmenu(bool &loadedGame) {
if (musicPlaying) {
//stop music
mainmenuMusic->stop(false);
+ delete mainmenuMusicFile;
musicPlaying = false;
}
-
-
switch (clickingOn) {
case MAINMENUHOTSPOT_START:
// Start game (actually exit main menu)
@@ -618,6 +687,11 @@ bool ToonEngine::showMainmenu(bool &loadedGame) {
}
}
+ //delete mainmenuMusic;
+ for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++)
+ delete entries[entryNr].animation;
+ delete mainmenuPicture;
+
return !exitGame;
}
@@ -631,13 +705,22 @@ Common::Error ToonEngine::run() {
initGraphics(640, 400, true);
init();
- // play producer intro
- getMoviePlayer()->play("MISC/VIELOGOM.SMK", 0x10);
-
- // show mainmenu
+ // do we need to load directly a game?
bool loadedGame = false;
- if (!showMainmenu(loadedGame)) {
- return Common::kNoError;
+ int32 slot = ConfMan.getInt("save_slot");
+ if (slot > -1) {
+ loadedGame = loadGame(slot);
+ }
+
+ if (!loadedGame) {
+
+ // play producer intro
+ getMoviePlayer()->play("MISC/VIELOGOM.SMK", 0x10);
+
+ // show mainmenu
+ if (!showMainmenu(loadedGame)) {
+ return Common::kNoError;
+ }
}
//loadScene(17);
@@ -657,10 +740,9 @@ ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
: Engine(syst), _gameDescription(gameDescription), _language(gameDescription->language) {
_system = syst;
_tickLength = 16;
- _currentMask = 0;
- _currentPicture = 0;
- _roomScaleData = 0;
- _shadowLUT = 0;
+ _currentPicture = NULL;
+ _currentMask = NULL;
+ _showConversationText = true;
_isDemo = _gameDescription->flags & ADGF_DEMO;
DebugMan.addDebugChannel(kDebugAnim, "Anim", "Animation debug level");
@@ -676,6 +758,48 @@ ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
DebugMan.addDebugChannel(kDebugTools, "Tools", "Tools debug level");
DebugMan.addDebugChannel(kDebugText, "Text", "Text debug level");
+ _resources = NULL;
+ _animationManager = NULL;
+ _moviePlayer = NULL;
+ _mainSurface = NULL;
+
+ _finalPalette = NULL;
+ _backupPalette = NULL;
+ _additionalPalette1 = NULL;
+ _additionalPalette2 = NULL;
+ _cutawayPalette = NULL;
+ _universalPalette = NULL;
+ _fluxPalette = NULL;
+
+ _roomScaleData = NULL;
+ _shadowLUT = NULL;
+
+ _conversationData = NULL;
+
+ _fontRenderer = NULL;
+ _fontToon = NULL;
+ _fontEZ = NULL;
+ _hotspots = NULL;
+ _genericTexts = NULL;
+ _roomTexts = NULL;
+ _script_func = NULL;
+ _script = NULL;
+
+ _saveBufferStream = NULL;
+
+ _pathFinding = NULL;
+ _console = new ToonConsole(this);
+
+ _cursorAnimation = NULL;
+ _cursorAnimationInstance = NULL;
+ _dialogIcons = NULL;
+ _inventoryIcons = NULL;
+ _inventoryIconSlots = NULL;
+ _genericTexts = NULL;
+ _audioManager = NULL;
+
+ memset(&_scriptData, 0, sizeof(EMCData));
+
switch (_language) {
case Common::EN_GRB:
case Common::EN_USA:
@@ -692,7 +816,7 @@ ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
_gameVariant = 3;
break;
case Common::ES_ESP:
- _gameVariant = 3;
+ _gameVariant = 4;
break;
default:
// 0 - english
@@ -702,7 +826,76 @@ ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
}
ToonEngine::~ToonEngine() {
+ delete _currentPicture;
+ delete _currentMask;
+
+ delete _resources;
+ delete _animationManager;
+ delete _moviePlayer;
+
+ if(_mainSurface) {
+ _mainSurface->free();
+ delete _mainSurface;
+ }
+
+ delete[] _finalPalette;
+ delete[] _backupPalette;
+ delete[] _additionalPalette1;
+ delete[] _additionalPalette2;
+ delete[] _cutawayPalette;
+ delete[] _universalPalette;
+ delete[] _fluxPalette;
+
+ delete[] _roomScaleData;
+ delete[] _shadowLUT;
+
+ delete[] _conversationData;
+
+ delete _fontRenderer;
+ delete _fontToon;
+ delete _fontEZ;
+ delete _hotspots;
+ delete _genericTexts;
+ delete _roomTexts;
+ delete _script_func;
+
+ _script->unload(&_scriptData);
+ delete _script;
+
+ delete _saveBufferStream;
+
+ delete _pathFinding;
+
+
+ for (int32 i = 0; i < 64; i++) {
+ if (_sceneAnimations[i]._active) {
+ // see if one character shares this instance
+ for (int32 c = 0; c < 32; c++) {
+ if (_characters[c] && _characters[c]->getAnimationInstance() == _sceneAnimations[i]._animInstance) {
+ _characters[c]->setAnimationInstance(0);
+ }
+ }
+ delete _sceneAnimations[i]._originalAnimInstance;
+ delete _sceneAnimations[i]._animation;
+ }
+ }
+ for (int32 i = 0; i < 32; i++)
+ delete _characters[i];
+
+ delete _cursorAnimation;
+ delete _cursorAnimationInstance;
+ delete _dialogIcons;
+ delete _inventoryIcons;
+ delete _inventoryIconSlots;
+ //delete _genericTexts;
+ delete _audioManager;
+ delete _gameState;
+
+ unloadToonDat();
+
+ DebugMan.clearAllDebugChannels();
+ delete _console;
}
void ToonEngine::flushPalette() {
@@ -728,21 +921,24 @@ void ToonEngine::setPaletteEntries(uint8 *palette, int32 offset, int32 num) {
_system->setPalette(vmpalette, offset, num);
}
-void ToonEngine::simpleUpdate() {
+void ToonEngine::simpleUpdate(bool waitCharacterToTalk) {
int32 elapsedTime = _system->getMillis() - _oldTimer2;
_oldTimer2 = _system->getMillis();
_oldTimer = _oldTimer2;
+ if (!_audioManager->voiceStillPlaying() && !waitCharacterToTalk) {
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ }
+
updateCharacters(elapsedTime);
updateAnimationSceneScripts(elapsedTime);
updateTimer(elapsedTime);
_animationManager->update(elapsedTime);
+ _audioManager->updateAmbientSFX();
render();
- if (!_audioManager->voiceStillPlaying()) {
- _currentTextLine = 0;
- _currentTextLineId = -1;
- }
+
}
void ToonEngine::fixPaletteEntries(uint8 *palette, int num) {
@@ -771,7 +967,7 @@ void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) {
do {
if (_sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() &&
- !_sceneAnimationScripts[_lastProcessedSceneScript]._frozen) {
+ !_sceneAnimationScripts[_lastProcessedSceneScript]._frozen && !_sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation) {
_animationSceneScriptRunFlag = true;
while (_animationSceneScriptRunFlag && _sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && !_shouldQuit) {
@@ -780,7 +976,7 @@ void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) {
//waitForScriptStep();
- if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen)
+ if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen || _sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation)
break;
}
@@ -807,7 +1003,6 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
char temp[256];
char temp2[256];
-
_firstFrame = true;
_gameState->_lastVisitedScene = _gameState->_currentScene;
@@ -849,25 +1044,28 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
_lastMouseButton = 0;
_mouseButton = 0;
- _currentHotspotItem = -1;
- _gameState->_sackVisible = true;
- _gameState->_inCloseUp = false;
- _gameState->_inConversation = false;
- _gameState->_inInventory = false;
- _gameState->_inCutaway = false;
- _gameState->_currentScrollValue = 0;
- _gameState->_currentScrollLock = false;
- _gameState->_inCloseUp = false;
+ _currentHotspotItem = 0;
+ if (!forGameLoad) {
+ _gameState->_sackVisible = true;
+ _gameState->_inCloseUp = false;
+ _gameState->_inConversation = false;
+ _gameState->_inInventory = false;
+ _gameState->_inCutaway = false;
+ _gameState->_currentScrollValue = 0;
+ _gameState->_currentScrollLock = false;
+ _gameState->_inCloseUp = false;
+ }
if (_gameState->_mouseState >= 0)
addItemToInventory(_gameState->_mouseState);
_gameState->_mouseState = -1;
-
+ _mouseButton = 0;
+ _lastMouseButton = 0x3;
// load package
- strcpy(temp, createRoomFilename(Common::String::printf("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
+ strcpy(temp, createRoomFilename(Common::String::format("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
resources()->openPackage(temp, true);
strcpy(temp, state()->_locations[SceneId]._name);
@@ -885,34 +1083,37 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
// load artwork
strcpy(temp, state()->_locations[SceneId]._name);
strcat(temp, ".cps");
+ delete _currentPicture;
_currentPicture = new Picture(this);
_currentPicture->loadPicture(temp);
_currentPicture->setupPalette();
strcpy(temp, state()->_locations[SceneId]._name);
strcat(temp, ".msc");
+ delete _currentMask;
_currentMask = new Picture(this);
if (_currentMask->loadPicture(temp))
_pathFinding->init(_currentMask);
strcpy(temp, state()->_locations[SceneId]._name);
strcat(temp, ".tre");
+ delete _roomTexts;
_roomTexts = new TextResource(this);
_roomTexts->loadTextResource(temp);
-
strcpy(temp, state()->_locations[SceneId]._name);
strcat(temp, ".dat");
uint32 fileSize;
uint8 *sceneData = resources()->getFileData(temp, &fileSize);
if (sceneData) {
+ delete[] _roomScaleData;
_roomScaleData = new uint8[fileSize];
memcpy(_roomScaleData, sceneData, fileSize);
}
strcpy(temp, state()->_locations[SceneId]._name);
strcat(temp, ".svi");
- strcpy(temp2, createRoomFilename(Common::String::printf("%s.svl", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
+ strcpy(temp2, createRoomFilename(Common::String::format("%s.svl", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
_audioManager->loadAudioPack(1, temp, temp2);
strcpy(temp, state()->_locations[SceneId]._name);
strcpy(temp2, state()->_locations[SceneId]._name);
@@ -931,7 +1132,6 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
_hotspots->LoadRif(temp, temp2);
restoreRifFlags(_gameState->_currentScene);
-
strcpy(temp, state()->_locations[SceneId]._name);
strcat(temp, ".cnv");
uint32 convfileSize;
@@ -953,7 +1153,7 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
_drew->update(0);
_flux->update(0);
-
+ _script->unload(&_scriptData);
_script->load(temp, &_scriptData, &_script_func->_opcodes);
_script->init(&_scriptState[0], &_scriptData);
_script->init(&_scriptState[1], &_scriptData);
@@ -970,6 +1170,7 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
_script->start(&_sceneAnimationScripts[i]._state, 9 + i);
_sceneAnimationScripts[i]._lastTimer = getSystem()->getMillis();
_sceneAnimationScripts[i]._frozen = false;
+ _sceneAnimationScripts[i]._frozenForConversation = false;
}
}
@@ -986,11 +1187,10 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
_lastProcessedSceneScript = 0;
_gameState->_locations[SceneId]._visited = true;
-
setupGeneralPalette();
createShadowLUT();
-
+ state()->_mouseHidden = false;
if (!forGameLoad) {
@@ -1005,11 +1205,11 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
waitForScriptStep();
if (_gameState->_nextSpecialEnterX != -1 && _gameState->_nextSpecialEnterY != -1) {
- _drew->setPosition(_gameState->_nextSpecialEnterX, _gameState->_nextSpecialEnterY);
+ _drew->forcePosition(_gameState->_nextSpecialEnterX, _gameState->_nextSpecialEnterY);
_gameState->_nextSpecialEnterX = -1;
_gameState->_nextSpecialEnterY = -1;
}
-
+
_script->start(&_scriptState[0], 3);
while (_script->run(&_scriptState[0]))
@@ -1021,8 +1221,6 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
waitForScriptStep();
}
-
- state()->_mouseHidden = false;
}
void ToonEngine::setupGeneralPalette() {
@@ -1074,6 +1272,7 @@ void ToonEngine::initChapter() {
memset(&data, 0, sizeof(data));
memset(&status, 0, sizeof(status));
+ delete _script;
_script = new EMCInterpreter(this);
_script->load("_START01.EMC", &data, &_script_func->_opcodes);
@@ -1082,13 +1281,17 @@ void ToonEngine::initChapter() {
while (_script->run(&status))
waitForScriptStep();
+ _script->unload(&data);
+
setupGeneralPalette();
}
void ToonEngine::loadCursor() {
+ delete _cursorAnimation;
_cursorAnimation = new Animation(this);
_cursorAnimation->loadAnimation("MOUSE.CAF");
+ delete _cursorAnimationInstance;
_cursorAnimationInstance = _animationManager->createNewInstance(kAnimationCursor);
_cursorAnimationInstance->setAnimation(_cursorAnimation);
_cursorAnimationInstance->setVisible(true);
@@ -1099,7 +1302,7 @@ void ToonEngine::loadCursor() {
setCursor(5);
}
-void ToonEngine::setCursor(int32 type, bool inventory, int32 offsetX, int32 offsetY) {
+void ToonEngine::setCursor(int32 type, bool inventory, int32 offsetX, int offsetY) {
static const int32 offsets[] = {
0, 1, 1, 6, 7, 1, 8, 10, 18, 10,
@@ -1172,9 +1375,9 @@ void ToonEngine::clickEvent() {
bool leftButton = false;
bool rightButton = false;
- if ((_lastMouseButton & 0x1) == 1 && (_mouseButton & 0x1) == 0)
+ if ((_lastMouseButton & 0x1) == 0 && (_mouseButton & 0x1) == 1)
leftButton = true;
- if ((_lastMouseButton & 0x2) == 2 && (_mouseButton & 0x2) == 0)
+ if ((_lastMouseButton & 0x2) == 0 && (_mouseButton & 0x2) == 2)
rightButton = true;
_lastMouseButton = _mouseButton;
@@ -1186,7 +1389,7 @@ void ToonEngine::clickEvent() {
if (_gameState->_mouseState >= 0 && !rightButton) {
addItemToInventory(_gameState->_mouseState);
setCursor(0, false, 0, 0);
- _currentHotspotItem = -1;
+ _currentHotspotItem = 0;
return;
} else {
showInventory();
@@ -1199,7 +1402,7 @@ void ToonEngine::clickEvent() {
if (rightButton && _gameState->_mouseState >= 0) {
addItemToInventory(_gameState->_mouseState);
setCursor(0, false, 0, 0);
- _currentHotspotItem = -1;
+ _currentHotspotItem = 0;
return;
}
@@ -1461,8 +1664,18 @@ void ToonEngine::exitScene() {
delete _sceneAnimations[i]._animation;
_sceneAnimations[i]._active = false;
_animationManager->removeInstance(_sceneAnimations[i]._animInstance);
- _sceneAnimations[i]._animInstance = 0;
- _sceneAnimations[i]._animation = 0;
+
+ // see if one character shares this instance
+ for (int32 c = 0; c < 32; c++) {
+ if (_characters[c] && _characters[c]->getAnimationInstance() == _sceneAnimations[i]._animInstance) {
+ _characters[c]->setAnimationInstance(NULL);
+ }
+ }
+
+ delete _sceneAnimations[i]._originalAnimInstance;
+ _sceneAnimations[i]._animInstance = NULL;
+ _sceneAnimations[i]._animation = NULL;
+ _sceneAnimations[i]._originalAnimInstance = NULL;
}
}
for (int32 i = 0; i < 64; i++) {
@@ -1492,10 +1705,18 @@ void ToonEngine::exitScene() {
_gameState->_mouseState = -1;
}
+ _audioManager->killAllAmbientSFX();
+ _audioManager->stopAllSfxs();
+ _audioManager->stopCurrentVoice();
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ _currentTextLineCharacterId = 0;
+
char temp[256];
- strcpy(temp, createRoomFilename(Common::String::printf("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
+ strcpy(temp, createRoomFilename(Common::String::format("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
resources()->closePackage(temp);
+
_drew->stopWalk();
_flux->stopWalk();
@@ -1580,6 +1801,10 @@ void ToonEngine::drawInfoLine() {
}
}
+Common::WriteStream *ToonEngine::getSaveBufferStream() {
+ return _saveBufferStream;
+}
+
const char *ToonEngine::getLocationString(int32 locationId, bool alreadyVisited) {
if (alreadyVisited)
return _locationDirVisited[locationId];
@@ -1744,9 +1969,11 @@ void ToonEngine::playTalkAnimOnCharacter(int32 animID, int32 characterId, bool t
int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) {
if (blocking == false && _audioManager->voiceStillPlaying()) {
- // someone is already talking, and this voice is not important
- // skip it
- return 0;
+ if (_currentTextLineCharacterId == 0 || _currentTextLineCharacterId == 1) {
+ // Drew or Flux is already talking, and this voice is not important
+ // skip it
+ return 0;
+ }
}
int32 myId = 0;
@@ -1832,7 +2059,19 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) {
_currentTextLineId = dialogid;
if (blocking) {
+ Character *character = getCharacterById(talkerId);
+ if (character)
+ character->setTalking(true);
+
playTalkAnimOnCharacter(talkerAnimId, talkerId, true);
+
+ // set once more the values, they may have been overwritten when the engine
+ // waits for the character to be ready.
+ _currentTextLine = myLine;
+ _currentTextLineCharacterId = talkerId;
+ _currentTextLineId = dialogid;
+
+
} else {
Character *character = getCharacterById(talkerId);
if (character)
@@ -1856,6 +2095,10 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) {
while (_audioManager->voiceStillPlaying() && !_shouldQuit)
doFrame();
_gameState->_mouseHidden = oldMouseHidden && _gameState->_mouseHidden;
+
+ Character *character = getCharacterById(talkerId);
+ if (character)
+ character->setTalking(false);
}
@@ -2017,10 +2260,10 @@ void ToonEngine::processConversationClick(Conversation *conv, int32 status) {
int16 *i = (int16 *)((char *)v2->_data4 + 2);
_gameState->_firstConverstationLine = false;
- while (*i >= 0) {
- if (*i < 100) {
+ while (READ_LE_INT16(i) >= 0) {
+ if (READ_LE_INT16(i) < 100) {
if (_gameState->_exitConversation == false) {
- characterTalk(i[1]);
+ characterTalk(READ_LE_INT16(i + 1));
}
} else {
runConversationCommand(&i);
@@ -2028,24 +2271,22 @@ void ToonEngine::processConversationClick(Conversation *conv, int32 status) {
i += 2;
}
- int16 command = i[0];
- int16 value = i[1];
+ int16 command = READ_LE_INT16(i);
+ int16 value = READ_LE_INT16(i + 1);
if (command == -1) {
v2->_data2 = 0;
} else if (command == -2) {
v2->_data4 = (char *)_conversationData + value;
- v2->_data3 = *(int16 *)v2->_data4;
+ v2->_data3 = READ_LE_INT16(v2->_data4);
} else if (command == -3) {
v2->_data2 = 2;
v2->_data4 = (char *)_conversationData + value;
- v2->_data3 = *(int16 *)v2->_data4;
+ v2->_data3 = READ_LE_INT16(v2->_data4);
}
int16 *v7 = i + 2;
-// Strangerke - Commented (not used)
-// int16 v6 = conv->state[0].data2;
- int16 v8 = *v7;
+ int16 v8 = READ_LE_INT16(v7);
if (v8 == -1) {
_gameState->_mouseHidden = false;
} else {
@@ -2057,14 +2298,14 @@ retry:
// find free dialogue slot
for (int j = 0; j < 10; j++) {
if (!conv->state[j]._data2) {
- conv->state[j]._data3 = *v14;
+ conv->state[j]._data3 = READ_LE_INT16(v14);
conv->state[j]._data4 = v14;
if (getConversationFlag(_gameState->_currentScene, conv->state[j]._data3))
conv->state[j]._data2 = 1;
else
conv->state[j]._data2 = 3;
- v8 = *v7;
+ v8 = READ_LE_INT16(v7);
if (v8 == -1)
return;
@@ -2158,31 +2399,31 @@ int32 ToonEngine::getConversationFlag(int32 locationId, int32 param) {
} else if (locationId == 0x10) {
switch (param) {
case 0x3e8:
- if (!(_gameState->_gameGlobalData[83] & 1))
+ if (!(_gameState->_gameGlobalData[30] & 1))
return 0;
break;
case 0x3e9:
- if (!(_gameState->_gameGlobalData[83] & 2))
+ if (!(_gameState->_gameGlobalData[30] & 2))
return 0;
break;
case 0x3ea:
- if (!(_gameState->_gameGlobalData[83] & 4))
+ if (!(_gameState->_gameGlobalData[30] & 4))
return 0;
break;
case 0x3eb:
- if (!(_gameState->_gameGlobalData[83] & 8))
+ if (!(_gameState->_gameGlobalData[30] & 8))
return 0;
break;
case 0x3ec:
- if (!(_gameState->_gameGlobalData[83] & 16))
+ if (!(_gameState->_gameGlobalData[30] & 16))
return 0;
break;
case 0x3ed:
- if (!(_gameState->_gameGlobalData[83] & 32))
+ if (!(_gameState->_gameGlobalData[30] & 32))
return 0;
break;
case 0x3ee:
- if (!(_gameState->_gameGlobalData[83] & 64))
+ if (!(_gameState->_gameGlobalData[30] & 64))
return 0;
break;
default:
@@ -2234,14 +2475,12 @@ int32 ToonEngine::getConversationFlag(int32 locationId, int32 param) {
return 1;
}
-int ToonEngine::runConversationCommand(int16 **command) {
+int32 ToonEngine::runConversationCommand(int16 **command) {
-// Strangerke - Commented (not used)
-// int16 com = **command;
int16 *v5 = *command;
- int v2 = v5[0];
- int v4 = v5[1];
+ int v2 = READ_LE_INT16(v5);
+ int v4 = READ_LE_INT16(v5+1);
int result = v2 - 100;
switch (v2) {
case 100:
@@ -2260,7 +2499,7 @@ int ToonEngine::runConversationCommand(int16 **command) {
//
case 105:
if (getConversationFlag(_gameState->_currentScene, v4)) {
- result = *(int16 *)(*command + 4);
+ result = READ_LE_INT16(*command + 4);
*command = (int16 *)((char *)_conversationData + result);
*command = (int16 *)((char *)_conversationData + result - 4);
} else {
@@ -2347,7 +2586,7 @@ int32 ToonEngine::showInventory() {
_gameState->_inInventory = true;
_gameState->_currentScrollValue = 0;
- int32 oldMouseButton = 0;
+ int32 oldMouseButton = 0x3;
int32 justPressedButton = 0;
_firstFrame = true;
@@ -2428,7 +2667,9 @@ int32 ToonEngine::showInventory() {
}
_gameState->_currentScrollValue = oldScrollValue;
- _gameState->_inInventory = false;
+ _gameState->_inInventory = false;
+ _mouseButton = 0;
+ _lastMouseButton = 0x3;
fadeOut(5);
if (_gameState->_inCloseUp) {
@@ -2571,6 +2812,7 @@ void ToonEngine::newGame() {
}
}
+
void ToonEngine::playSFX(int32 id, int32 volume) {
if (id < 0)
_audioManager->playSFX(-id + 1, volume, true);
@@ -2657,18 +2899,56 @@ Character *ToonEngine::getCharacterById(int32 charId) {
}
void ToonEngine::drawConversationLine() {
- if (_currentTextLine) {
+ if (_currentTextLine && _showConversationText) {
_fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
_fontRenderer->setFont(_fontToon);
_fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, Common::String(_currentTextLine), 0);
}
}
+void ToonEngine::pauseEngineIntern(bool pause) {
+
+ Engine::pauseEngineIntern(pause);
+
+ static int32 pauseStart = 0;
+ if (pause) {
+ pauseStart = _system->getMillis();
+
+ } else {
+ _oldTimer = _system->getMillis();
+ _oldTimer2 = _oldTimer;
+
+ int32 diff = _oldTimer - pauseStart;
+
+ // we have to add the difference between the start and the current time
+ // to all "timer based" values.
+ for (int32 i = 0; i < _gameState->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
+ _sceneAnimationScripts[i]._lastTimer += diff;
+ }
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i]) {
+ _characters[i]->updateTimers(diff);
+ }
+ }
+
+ _gameState->_timerTimeout[0] += diff;
+ _gameState->_timerTimeout[1] += diff;
+ }
+}
+
+bool ToonEngine::canSaveGameStateCurrently() {
+ return !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
+}
+
+bool ToonEngine::canLoadGameStateCurrently() {
+ return !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
+}
+
Common::String ToonEngine::getSavegameName(int nr) {
- return _targetName + Common::String::printf(".%03d", nr);
+ return _targetName + Common::String::format(".%03d", nr);
}
-bool ToonEngine::saveGame(int32 slot) {
+bool ToonEngine::saveGame(int32 slot, Common::String saveGameDesc) {
const EnginePlugin *plugin = NULL;
int16 savegameId;
Common::String savegameDescription;
@@ -2682,7 +2962,11 @@ bool ToonEngine::saveGame(int32 slot) {
delete dialog;
} else {
savegameId = slot;
- savegameDescription = Common::String::printf("Quick save #%d", slot);
+ if (!saveGameDesc.empty()) {
+ savegameDescription = saveGameDesc;
+ } else {
+ savegameDescription = Common::String::format("Quick save #%d", slot);
+ }
}
if (savegameId < 0)
@@ -2821,8 +3105,9 @@ bool ToonEngine::loadGame(int32 slot) {
for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
_sceneAnimationScripts[i]._active = loadFile->readByte();
_sceneAnimationScripts[i]._frozen = loadFile->readByte();
+ _sceneAnimationScripts[i]._frozenForConversation = false;
int32 oldTimer = loadFile->readSint32BE();
- _sceneAnimationScripts[i]._lastTimer = MAX(0,oldTimer + timerDiff);
+ _sceneAnimationScripts[i]._lastTimer = MAX<int32>(0,oldTimer + timerDiff);
_script->loadState(&_sceneAnimationScripts[i]._state, loadFile);
}
@@ -4181,7 +4466,7 @@ int32 ToonEngine::pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait
}
Common::String ToonEngine::createRoomFilename(Common::String name) {
- Common::String file = Common::String::printf("ACT%d/%s/%s", _gameState->_currentChapter, _gameState->_locations[_gameState->_currentScene]._name, name.c_str());
+ Common::String file = Common::String::format("ACT%d/%s/%s", _gameState->_currentChapter, _gameState->_locations[_gameState->_currentScene]._name, name.c_str());
return file;
}
@@ -4277,14 +4562,20 @@ bool ToonEngine::loadToonDat() {
_numVariant = in.readUint16BE();
- _locationDirNotVisited = loadTextsVariante(in);
- _locationDirVisited = loadTextsVariante(in);
- _specialInfoLine = loadTextsVariante(in);
+ _locationDirNotVisited = loadTextsVariants(in);
+ _locationDirVisited = loadTextsVariants(in);
+ _specialInfoLine = loadTextsVariants(in);
return true;
}
-char **ToonEngine::loadTextsVariante(Common::File &in) {
+void ToonEngine::unloadToonDat() {
+ unloadTextsVariants(_locationDirNotVisited);
+ unloadTextsVariants(_locationDirVisited);
+ unloadTextsVariants(_specialInfoLine);
+}
+
+char **ToonEngine::loadTextsVariants(Common::File &in) {
int numTexts;
int entryLen;
int len;
@@ -4302,6 +4593,8 @@ char **ToonEngine::loadTextsVariante(Common::File &in) {
res[0] += DATAALIGNMENT;
} else {
in.read(pos, entryLen);
+ free(pos);
+ continue;
}
pos += DATAALIGNMENT;
@@ -4320,6 +4613,14 @@ char **ToonEngine::loadTextsVariante(Common::File &in) {
return res;
}
+void ToonEngine::unloadTextsVariants(char **texts) {
+ if (!texts)
+ return;
+
+ free(*texts - DATAALIGNMENT);
+ free(texts);
+}
+
void ToonEngine::makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2) {
_currentMask->drawLineOnMask(x, y, x2, y2, false);
}
@@ -4374,6 +4675,7 @@ void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) {
_animInstance = vm->getAnimationManager()->createNewInstance(kAnimationScene);
_animInstance->load(stream);
vm->getAnimationManager()->addInstance(_animInstance);
+ _originalAnimInstance = _animInstance;
}
// load animation if any
@@ -4391,10 +4693,9 @@ void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) {
_animation = new Animation(vm);
_animation->loadAnimation(animationName);
- if (_animInstance)
+ if (_animInstance) {
_animInstance->setAnimation(_animation, false);
-
- printf("load animation instance %d / %s / visible %d \n", _id, _animation->_name, _animInstance->getVisible());
+ }
}
}
diff --git a/engines/toon/toon.h b/engines/toon/toon.h
index 30aa344517..05b2bac47c 100644
--- a/engines/toon/toon.h
+++ b/engines/toon/toon.h
@@ -30,6 +30,7 @@
#include "engines/engine.h"
#include "graphics/surface.h"
#include "common/random.h"
+#include "common/error.h"
#include "toon/resource.h"
#include "toon/script.h"
#include "toon/script_func.h"
@@ -40,13 +41,27 @@
#include "toon/font.h"
#include "toon/text.h"
#include "toon/audio.h"
+#include "toon/console.h"
+
+namespace Common {
+class MemoryWriteStreamDynamic;
+}
#define TOON_DAT_VER_MAJ 0 // 1 byte
#define TOON_DAT_VER_MIN 3 // 1 byte
#define TOON_SAVEGAME_VERSION 4
#define DATAALIGNMENT 4
+/**
+ * This is the namespace of the Toon engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Toonstruck
+ */
namespace Toon {
+
enum ToonGameType {
GType_TOON = 1
};
@@ -91,10 +106,13 @@ public:
char **_specialInfoLine;
Common::Error run();
+ GUI::Debugger *getDebugger() { return _console; }
bool showMainmenu(bool &loadedGame);
void init();
bool loadToonDat();
- char **loadTextsVariante(Common::File &in);
+ char **loadTextsVariants(Common::File &in);
+ void unloadTextsVariants(char **texts);
+ void unloadToonDat();
void setPaletteEntries(uint8 *palette, int32 offset, int32 num);
void fixPaletteEntries(uint8 *palette, int num);
void flushPalette();
@@ -134,7 +152,7 @@ public:
int32 runConversationCommand(int16 **command);
void prepareConversations();
void drawConversationIcons();
- void simpleUpdate();
+ void simpleUpdate(bool waitCharacterToTalk = false);
int32 waitTicks(int32 numTicks, bool breakOnMouseClick);
void copyToVirtualScreen(bool updateScreen = true);
void getMouseEvent();
@@ -160,7 +178,7 @@ public:
Character *getCharacterById(int32 charId);
Common::String getSavegameName(int nr);
bool loadGame(int32 slot);
- bool saveGame(int32 slot);
+ bool saveGame(int32 slot, Common::String saveGameDesc);
void fadeIn(int32 numFrames) ;
void fadeOut(int32 numFrames) ;
void initCharacter(int32 characterId, int32 animScriptId, int32 animToPlayId, int32 sceneAnimationId);
@@ -186,6 +204,13 @@ public:
const char *getSpecialConversationMusic(int32 locationId);
void playRoomMusic();
void waitForScriptStep();
+ void doMagnifierEffect();
+
+
+
+ bool canSaveGameStateCurrently();
+ bool canLoadGameStateCurrently();
+ void pauseEngineIntern(bool pause);
Resources *resources() {
return _resources;
@@ -243,6 +268,10 @@ public:
return _currentTextLineId;
}
+ int32 getCurrentCharacterTalking() {
+ return _currentTextLineCharacterId;
+ }
+
CharacterDrew *getDrew() {
return (CharacterDrew *)_drew;
}
@@ -283,14 +312,28 @@ public:
return _pathFinding;
}
- Common::WriteStream *getSaveBufferStream() {
- return _saveBufferStream;
- }
+ Common::WriteStream *getSaveBufferStream();
bool shouldQuitGame() const {
return _shouldQuit;
}
+ Common::Error saveGameState(int slot, const char *desc) {
+
+ return (saveGame(slot, desc) ? Common::kWritingFailed : Common::kNoError);
+ }
+
+ Common::Error loadGameState(int slot) {
+ return (loadGame(slot) ? Common::kReadingFailed : Common::kNoError);
+ }
+
+ bool hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+ }
+
protected:
OSystem *_system;
int32 _tickLength;
@@ -381,6 +424,9 @@ protected:
bool _firstFrame;
bool _isDemo;
+ bool _showConversationText;
+private:
+ ToonConsole *_console;
};
} // End of namespace Toon
diff --git a/engines/touche/console.cpp b/engines/touche/console.cpp
new file mode 100644
index 0000000000..9d2b94d31c
--- /dev/null
+++ b/engines/touche/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "touche/console.h"
+#include "touche/touche.h"
+
+namespace Touche {
+
+ToucheConsole::ToucheConsole(ToucheEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+ToucheConsole::~ToucheConsole() {
+}
+
+void ToucheConsole::preEnter() {
+}
+
+void ToucheConsole::postEnter() {
+}
+
+} // End of namespace Touche
diff --git a/engines/touche/console.h b/engines/touche/console.h
new file mode 100644
index 0000000000..bcb947cf2e
--- /dev/null
+++ b/engines/touche/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef TOUCHE_CONSOLE_H
+#define TOUCHE_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Touche {
+
+class ToucheEngine;
+
+class ToucheConsole : public GUI::Debugger {
+public:
+ ToucheConsole(ToucheEngine *vm);
+ virtual ~ToucheConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ ToucheEngine *_vm;
+};
+
+} // End of namespace Touche
+
+#endif
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index 35f03fa657..06d15664a5 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -130,13 +130,21 @@ static const char *directoryGlobs[] = {
};
static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
(const byte *)Touche::gameDescriptions,
+ // Size of that superset structure
sizeof(ADGameDescription),
- 4096, // number of md5 bytes
+ // Number of bytes to compute MD5 sum for
+ 4096,
+ // List of all engine targets
toucheGames,
- 0, // no obsolete targets data
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
"touche",
- Touche::fileBasedFallback, // file-based detection data to enable not yet known versions to start
+ // List of files for file-based fallback detection (optional)
+ Touche::fileBasedFallback,
+ // Flags
kADFlagPrintWarningOnFileBasedFallback,
// Additional GUI options (for every game}
Common::GUIO_NONE,
diff --git a/engines/touche/module.mk b/engines/touche/module.mk
index 30965e1155..710b3169de 100644
--- a/engines/touche/module.mk
+++ b/engines/touche/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/touche
MODULE_OBJS := \
+ console.o \
detection.o \
graphics.o \
menu.o \
diff --git a/engines/touche/resource.cpp b/engines/touche/resource.cpp
index 8a810f09d8..f76084d556 100644
--- a/engines/touche/resource.cpp
+++ b/engines/touche/resource.cpp
@@ -666,7 +666,7 @@ void ToucheEngine::res_loadSpeechSegment(int num) {
return;
}
_fSpeech[0].seek(offs);
- Common::MemoryReadStream *tmp = _fSpeech[0].readStream(size);
+ Common::SeekableReadStream *tmp = _fSpeech[0].readStream(size);
if (tmp)
stream = (compressedSpeechFilesTable[_compressedSpeechData].makeStream)(tmp, DisposeAfterUse::YES);
}
diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp
index 2dc8b76b4f..0bfb43a27a 100644
--- a/engines/touche/touche.cpp
+++ b/engines/touche/touche.cpp
@@ -82,11 +82,15 @@ ToucheEngine::ToucheEngine(OSystem *system, Common::Language language)
DebugMan.addDebugChannel(kDebugOpcodes, "Opcodes", "Opcodes debug level");
DebugMan.addDebugChannel(kDebugMenu, "Menu", "Menu debug level");
+ _console = new ToucheConsole(this);
+
g_eventRec.registerRandomSource(_rnd, "touche");
}
ToucheEngine::~ToucheEngine() {
DebugMan.clearAllDebugChannels();
+ delete _console;
+
delete _midiPlayer;
}
@@ -324,6 +328,9 @@ void ToucheEngine::processEvents(bool handleKeyEvents) {
if (event.kbd.hasFlags(Common::KBD_CTRL)) {
if (event.kbd.keycode == Common::KEYCODE_f) {
_fastMode = !_fastMode;
+ } else if (event.kbd.keycode == Common::KEYCODE_d) {
+ this->getDebugger()->attach();
+ this->getDebugger()->onFrame();
}
} else {
if (event.kbd.ascii == 't') {
diff --git a/engines/touche/touche.h b/engines/touche/touche.h
index 33ca81ed4f..926dab04b2 100644
--- a/engines/touche/touche.h
+++ b/engines/touche/touche.h
@@ -37,13 +37,15 @@
#include "engines/engine.h"
+#include "touche/console.h"
+
/**
* This is the namespace of the Touche engine.
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Touche: The Adventures of the Fifth Musketeer
*/
namespace Touche {
@@ -382,6 +384,7 @@ public:
virtual Common::Error run();
virtual bool hasFeature(EngineFeature f) const;
virtual void syncSoundSettings();
+ GUI::Debugger *getDebugger() { return _console; }
protected:
@@ -518,6 +521,8 @@ protected:
virtual bool canLoadGameStateCurrently();
virtual bool canSaveGameStateCurrently();
+ ToucheConsole *_console;
+
void setupOpcodes();
void op_nop();
void op_jnz();
diff --git a/engines/tucker/console.cpp b/engines/tucker/console.cpp
new file mode 100644
index 0000000000..f06fd8fddb
--- /dev/null
+++ b/engines/tucker/console.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "tucker/console.h"
+#include "tucker/tucker.h"
+
+namespace Tucker {
+
+TuckerConsole::TuckerConsole(TuckerEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+TuckerConsole::~TuckerConsole() {
+}
+
+void TuckerConsole::preEnter() {
+}
+
+void TuckerConsole::postEnter() {
+}
+
+} // End of namespace Tucker
diff --git a/engines/tucker/console.h b/engines/tucker/console.h
new file mode 100644
index 0000000000..2449fa3279
--- /dev/null
+++ b/engines/tucker/console.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef TUCKER_CONSOLE_H
+#define TUCKER_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Tucker {
+
+class TuckerEngine;
+
+class TuckerConsole : public GUI::Debugger {
+public:
+ TuckerConsole(TuckerEngine *vm);
+ virtual ~TuckerConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ TuckerEngine *_vm;
+};
+
+} // End of namespace Tucker
+
+#endif
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index 0a9dec9b46..f0437d2f87 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -106,16 +106,27 @@ static const ADGameDescription tuckerGameDescriptions[] = {
};
static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
(const byte *)tuckerGameDescriptions,
+ // Size of that superset structure
sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
512,
+ // List of all engine targets
tuckerGames,
+ // Structure for autoupgrading obsolete targets
0,
+ // Name of single gameid (optional)
"tucker",
+ // List of files for file-based fallback detection (optional)
0,
+ // Flags
0,
- Common::GUIO_NONE,
+ // Additional GUI options (for every game}
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
1,
+ // List of directory globs
0
};
diff --git a/engines/tucker/module.mk b/engines/tucker/module.mk
index 4847fdc3ec..d11c68b746 100644
--- a/engines/tucker/module.mk
+++ b/engines/tucker/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/tucker
MODULE_OBJS := \
+ console.o \
detection.o \
graphics.o \
locations.o \
diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp
index 025721e5fc..1d1daf5adc 100644
--- a/engines/tucker/resource.cpp
+++ b/engines/tucker/resource.cpp
@@ -268,7 +268,7 @@ Audio::RewindableAudioStream *CompressedSound::load(CompressedSoundType type, in
int soundSize = _fCompressedSound.readUint32LE();
if (soundSize != 0) {
_fCompressedSound.seek(dirOffset + dirSize * 8 + soundOffset);
- Common::MemoryReadStream *tmp = _fCompressedSound.readStream(soundSize);
+ Common::SeekableReadStream *tmp = _fCompressedSound.readStream(soundSize);
if (tmp) {
stream = (compressedSoundFilesTable[_compressedSoundType].makeStream)(tmp, DisposeAfterUse::YES);
}
diff --git a/engines/tucker/tucker.cpp b/engines/tucker/tucker.cpp
index c86991ab1b..2b9b4edee4 100644
--- a/engines/tucker/tucker.cpp
+++ b/engines/tucker/tucker.cpp
@@ -38,9 +38,11 @@ namespace Tucker {
TuckerEngine::TuckerEngine(OSystem *system, Common::Language language, uint32 flags)
: Engine(system), _gameLang(language), _gameFlags(flags) {
+ _console = new TuckerConsole(this);
}
TuckerEngine::~TuckerEngine() {
+ delete _console;
}
bool TuckerEngine::hasFeature(EngineFeature f) const {
@@ -628,6 +630,12 @@ void TuckerEngine::parseEvents() {
case Common::KEYCODE_ESCAPE:
_inputKeys[kInputKeyEscape] = true;
break;
+ case Common::KEYCODE_d:
+ if (ev.kbd.hasFlags(Common::KBD_CTRL)) {
+ this->getDebugger()->attach();
+ this->getDebugger()->onFrame();
+ }
+ break;
default:
break;
}
diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h
index 86f5843e77..a7d7f1cdd4 100644
--- a/engines/tucker/tucker.h
+++ b/engines/tucker/tucker.h
@@ -39,12 +39,14 @@
#include "engines/engine.h"
+#include "tucker/console.h"
+
/**
* This is the namespace of the Tucker engine.
*
* Status of this engine: Complete
*
- * Supported games:
+ * Games using this engine:
* - Bud Tucker in Double Trouble
*/
namespace Tucker {
@@ -275,6 +277,7 @@ public:
virtual Common::Error run();
virtual bool hasFeature(EngineFeature f) const;
virtual void syncSoundSettings();
+ GUI::Debugger *getDebugger() { return _console; }
protected:
@@ -566,6 +569,8 @@ protected:
virtual bool canLoadGameStateCurrently();
virtual bool canSaveGameStateCurrently();
+ TuckerConsole *_console;
+
void handleIntroSequence();
void handleCreditsSequence();
void handleCongratulationsSequence();