/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "m4/burger/gui/gui_gizmo.h" #include "m4/burger/vars.h" #include "m4/core/errors.h" #include "m4/core/imath.h" #include "m4/adv_r/adv_background.h" #include "m4/adv_r/adv_control.h" #include "m4/graphics/gr_sprite.h" #include "m4/gui/gui_event.h" #include "m4/gui/gui_sys.h" #include "m4/gui/gui_vmng.h" #include "m4/mem/mem.h" #include "m4/platform/keys.h" #include "m4/m4.h" #include "m4/platform/timer.h" namespace M4 { namespace Burger { namespace GUI { static void gizmo_dispose_gui(); static void gizmo_restore_interface(bool fade); static void gizmo_free_gui(Gizmo *gizmo); static bool gizmo_load_sprites(const char *name, size_t count); static void gizmo_free_sprites(); static void gizmo_draw_sprite(M4sprite *sprite, Buffer *dest, int destX, int destY); static Gizmo *gui_create_gizmo(M4sprite *sprite, int sx, int sy, uint scrnFlags); static void gizmo_digi_daemon(int trigger); static void gizmo_daemon(int trigger); Gizmo_Globals::Gizmo_Globals() { Common::fill(_roomFlags, _roomFlags + 15, false); } Gizmo_Globals::~Gizmo_Globals() { mem_free(_gui); } static void gizmo_sound() { digi_read_another_chunk(); midi_loop(); g_system->updateScreen(); g_system->delayMillis(10); g_events->process(); gui_system_event_handler(); } void gizmo_digi_play(const char *name, int vol, bool &done) { if (!done) { done = true; digi_play(name, 2, vol); digi_read_another_chunk(); player_set_commands_allowed(false); while (!g_engine->shouldQuit() && digi_play_state(2)) gizmo_sound(); player_set_commands_allowed(true); } } void gizmo_initialize(RGB8 *pal) { if (!_GIZMO(initialized)) { _GIZMO(initialized) = true; // Remember whether interface was visible when gizmo was initialized if (INTERFACE_VISIBLE) { _GIZMO(interface_visible) = true; interface_hide(); } else { _GIZMO(interface_visible) = false; } _GIZMO(palette) = pal; krn_fade_to_grey(pal, 5, 1); } } void gizmo_shutdown(void *, void *) { gizmo_dispose_gui(); gizmo_restore_interface(true); } static void gizmo_digi_daemon(int trigger) { switch (trigger) { case 5000: if (player_been_here(503)) { if (_G(flags)[kBORK_STATE] == 16) gizmo_digi_play("510w005", 255, _GIZMO(roomFlags)[3]); else gizmo_digi_play("510w004", 255, _GIZMO(roomFlags)[2]); } break; case 5001: if (player_been_here(507)) { if (_G(flags)[V223] == 2) gizmo_digi_play("510w008", 255, _GIZMO(roomFlags)[5]); else gizmo_digi_play("510w007", 255, _GIZMO(roomFlags)[4]); } break; case 5002: if (player_been_here(504)) { if (_G(flags)[V210] == 5002) gizmo_digi_play("510w011", 255, _GIZMO(roomFlags)[7]); else gizmo_digi_play("510w010", 255, _GIZMO(roomFlags)[6]); } break; case 5003: if (player_been_here(508)) { if (_G(flags)[V227] != 0) gizmo_digi_play("510w014", 255, _GIZMO(roomFlags)[9]); else gizmo_digi_play("510w013", 255, _GIZMO(roomFlags)[8]); } break; case 5004: if (player_been_here(506)) { if (_G(flags)[V218] == 5003) gizmo_digi_play("510w017", 255, _GIZMO(roomFlags)[11]); else gizmo_digi_play("510w016", 255, _GIZMO(roomFlags)[10]); } break; case 5005: if (_G(flags)[kStairsBorkState] == 5003) gizmo_digi_play("510w019", 255, _GIZMO(roomFlags)[13]); else gizmo_digi_play("510w018", 255, _GIZMO(roomFlags)[12]); break; case 5006: gizmo_digi_play("510w020", 255, _GIZMO(roomFlags)[14]); break; default: break; } } static void gizmo_restore_sprite(int spriteIndex) { if (!_GIZMO(gui)) return; Buffer *dest = _GIZMO(gui)->_grBuff->get_buffer(); if (!dest) return; if (spriteIndex >= 22) gizmo_draw_sprite(_GIZMO(sprites)[spriteIndex], dest, 270, 38); int32 status = 0; ScreenContext *ctx = vmng_screen_find(_GIZMO(gui), &status); if (ctx && status == 1) RestoreScreens(270, 38, 381, 93); } static void gizmo_digi_wait(int spriteIndex1, int spriteIndex2) { player_set_commands_allowed(false); digi_read_another_chunk(); int spriteNum = spriteIndex1; while (digi_play_state(2)) { // Cycle displayed sprite gizmo_restore_sprite(spriteNum); spriteNum = (spriteNum == spriteIndex2) ? spriteIndex1 : spriteNum + 1; uint32 timer = timer_read_60(); while (!g_engine->shouldQuit() && (timer_read_60() - timer) < 6) gizmo_sound(); } gizmo_restore_sprite(57); player_set_commands_allowed(true); } static void gizmo_daemon(int trigger) { switch (trigger) { case 5000: switch (imath_ranged_rand(1, 3)) { case 1: digi_play("510b001a", 2, 255, -1); break; case 2: digi_play("510b001b", 2, 255, -1); break; default: digi_play("510b001c", 2, 255, -1); break; } gizmo_digi_wait(32, 36); break; case 5001: switch (imath_ranged_rand(1, 3)) { case 1: digi_play("510b002a", 2, 255, -1); break; case 2: digi_play("510b002b", 2, 255, -1); break; default: digi_play("510b002c", 2, 255, -1); break; } gizmo_digi_wait(27, 31); break; case 5002: switch (imath_ranged_rand(1, 3)) { case 1: digi_play("510b003a", 2, 255, -1); break; case 2: digi_play("510b003b", 2, 255, -1); break; default: digi_play("510b003c", 2, 255, -1); break; } gizmo_digi_wait(32, 36); break; case 5003: switch (imath_ranged_rand(1, 2)) { case 1: digi_play("510b004a", 2, 255, -1); break; case 2: digi_play("510b004b", 2, 255, -1); break; default: break; } gizmo_digi_wait(37, 41); break; case 5004: switch (imath_ranged_rand(1, 3)) { case 1: digi_play("510b005a", 2, 255, -1); break; case 2: digi_play("510b005b", 2, 255, -1); break; default: digi_play("510b005c", 2, 255, -1); break; } gizmo_digi_wait(42, 46); break; case 5005: switch (imath_ranged_rand(1, 3)) { case 1: digi_play("510b006a", 2, 255, -1); break; case 2: digi_play("510b006b", 2, 255, -1); break; default: digi_play("510b006c", 2, 255, -1); break; } gizmo_digi_wait(47, 51); break; case 5006: switch (imath_ranged_rand(1, 3)) { case 1: digi_play("510b007a", 2, 255, -1); break; case 2: digi_play("510b007b", 2, 255, -1); break; default: digi_play("510b007c", 2, 255, -1); break; } gizmo_digi_wait(52, 56); break; default: break; } gizmo_digi_daemon(trigger); } static void gizmo_restore_interface(bool fade) { if (_GIZMO(initialized)) { _GIZMO(currentItem) = nullptr; if (_GIZMO(lowMemory2)) { if (!adv_restoreBackground()) error_show(FL, 0, "unable to restore background"); } if (_GIZMO(lowMemory1)) { if (!adv_restoreCodes()) error_show(FL, 0, "unable to restore screen codes"); } krn_fade_from_grey(_GIZMO(palette), 5, 1, fade ? 1 : 2); if (_GIZMO(interface_visible)) interface_show(); mouse_unlock_sprite(); game_pause(false); _GIZMO(initialized) = false; } } static void gizmo_dispose_gui() { if (_GIZMO(gui)) { vmng_screen_dispose(_GIZMO(gui)); gizmo_free_gui(_GIZMO(gui)); gizmo_free_sprites(); _GIZMO(gui) = nullptr; } } static void gizmo_free_gui(Gizmo *gizmo) { if (!gizmo) return; for (GizmoItem *item = gizmo->_items; item; item = gizmo->_items) { gizmo->_items = item->_next; (*item->_fnFree)(item); } GrBuff *grBuff = gizmo->_grBuff; if (grBuff) delete grBuff; mem_free(gizmo); } static bool gizmo_load_sprites(const char *name, size_t count) { if (LoadSpriteSeries(name, &_GIZMO(seriesHandle), &_GIZMO(celsOffset), &_GIZMO(palOffset), _GIZMO(palette)) > 0) { gr_pal_set_range(_GIZMO(palette), 64, 192); _GIZMO(assetName) = mem_strdup(name); _GIZMO(spriteCount) = count; _GIZMO(sprites) = (M4sprite **)mem_alloc(count * sizeof(M4sprite *), "*sprites array"); for (size_t idx = 0; idx < count; ++idx) { _GIZMO(sprites)[idx] = CreateSprite(_GIZMO(seriesHandle), _GIZMO(celsOffset), idx, nullptr, nullptr); } return true; } return false; } static void gizmo_free_sprites() { if (_GIZMO(assetName)) { rtoss(_GIZMO(assetName)); mem_free(_GIZMO(assetName)); _GIZMO(assetName) = nullptr; _GIZMO(seriesHandle) = nullptr; _GIZMO(celsOffset) = -1; _GIZMO(palOffset) = -1; for (int idx = 0; idx < _GIZMO(spriteCount); ++idx) mem_free(_GIZMO(sprites)[idx]); mem_free(_GIZMO(sprites)); _GIZMO(sprites) = nullptr; _GIZMO(spriteCount) = 0; } } void gizmo_draw_sprite(M4sprite *sprite, Buffer *dest, int destX, int destY) { Buffer src; DrawRequest dr; if (sprite && dest) { HLock(sprite->sourceHandle); sprite->data = (uint8 *)((intptr)*sprite->sourceHandle + sprite->sourceOffset); src.stride = src.w = sprite->w; src.h = sprite->h; src.encoding = sprite->encoding & 0x7f; src.data = sprite->data; dr.Src = &src; dr.Dest = dest; dr.x = destX; dr.y = destY; dr.scaleX = dr.scaleY = 100; dr.srcDepth = 0; dr.depthCode = nullptr; dr.Pal = nullptr; dr.ICT = nullptr; gr_sprite_draw(&dr); HUnLock(sprite->sourceHandle); } } static void gizmo_draw(ScreenContext *s, RectList *r, Buffer *dest, int32 destX, int32 destY) { if (!s) return; void *scrnContent = s->scrnContent; if (!scrnContent) return; Gizmo *gizmo = (Gizmo *)s->scrnContent; if (!gizmo) return; Buffer *src = gizmo->_grBuff->get_buffer(); if (!src) return; if (dest) { for (RectList *rect = r; rect; rect = rect->next) { gr_buffer_rect_copy_2(src, dest, rect->x1 - s->x1, rect->y1 - s->y1, destX, destY, rect->x2 - rect->x1 + 1, rect->y2 - rect->y1 + 1); } } else { for (RectList *rect = r; rect; rect = rect->next) { vmng_refresh_video(rect->x1, rect->y1, rect->x1 - s->x1, rect->y1 - s->y1, rect->x2 - s->x1, rect->y2 - s->y1, src); } } } static bool gizmo_eventHandler(void *s, int32 eventType, int32 event, int32 x, int32 y, bool *z) { Gizmo *gizmo = (Gizmo *)s; bool flag = false; int32 status = 0; ScreenContext *ctx = vmng_screen_find(s, &status); // WORKAROUND: gui_system_event_handler may call without z being set bool dummyZ = false; if (!z) z = &dummyZ; *z = false; if (!ctx || status != 1) return false; if (eventType == EVENT_KEY) { if (event == KEY_ESCAPE && gizmo->_fnEscape) { _GIZMO(currentItem) = nullptr; (*gizmo->_fnEscape)(); return true; } else if (event == KEY_RETURN && gizmo->_fnEnter) { _GIZMO(currentItem) = nullptr; (*gizmo->_fnEnter)(); return true; } } const int xs = x + ctx->x1; const int ys = y + ctx->y1; if (_GIZMO(currentItem)) { flag = (*_GIZMO(currentItem)->_fnEvents)(_GIZMO(currentItem), eventType, event, xs, ys, &_GIZMO(currentItem)); if (_GIZMO(currentItem)) *z = true; if (flag) return true; } if (eventType == EVENT_MOUSE) { GizmoItem *item; for (item = gizmo->_items; item; item = item->_next) { if (item->_btnRect.contains(xs, ys)) break; } if (item && item->_fnEvents) { item->_fnEvents(item, eventType, event, xs, ys, &_GIZMO(currentItem)); if (_GIZMO(currentItem)) *z = true; return true; } } else if (eventType == EVENT_KEY) { for (GizmoItem *item = gizmo->_items; item && !flag; item = item->_next) { if (item->_fnEvents) flag = (*item->_fnEvents)(item, eventType, event, -1, -1, nullptr); } return flag; } switch (event) { case 2: case 14: if (!(ctx->scrnFlags & SF_IMMOVABLE)) { *z = true; _GIZMO(savedFlag) = true; _GIZMO(savedX) = x; _GIZMO(savedY) = y; } if (xs < 230 || xs > 420 || ys > 374) gizmo_shutdown(); break; case 4: case 16: if (_GIZMO(savedFlag)) { MoveScreenDelta(ctx, _GIZMO(savedX), _GIZMO(savedY)); _GIZMO(savedX) = x; _GIZMO(savedY) = y; } break; case 5: case 17: *z = false; _GIZMO(savedFlag) = false; break; default: break; } return true; } static void gizmo_item_draw(GizmoItem *item, Gizmo *gizmo, int x, int y) { if (!item || !item->_button || !gizmo) return; Buffer *src = nullptr; if (item->_hasBuffer) { if (!item->_grBuff) return; src = item->_grBuff->get_buffer(); if (!src) return; } Buffer *dest = gizmo->_grBuff->get_buffer(); if (!dest) return; GizmoButton *btn = item->_button; M4sprite *sprite = nullptr; switch (btn->_state) { case IN_CONTROL: sprite = _GIZMO(sprites)[15 + btn->_index]; break; case OVER_CONTROL: sprite = _GIZMO(sprites)[8 + btn->_index]; break; default: sprite = _GIZMO(sprites)[1 + btn->_index]; break; } if (src) { gr_buffer_rect_copy_2(src, dest, 0, 0, x, y, src->w, src->h); item->_grBuff->release(); } gizmo_draw_sprite(sprite, dest, x, y); gizmo->_grBuff->release(); } static void gizmo_item_free(GizmoItem *item) { if (!item) return; if (item->_grBuff) delete item->_grBuff; if (item->_button) mem_free(item->_button); mem_free(item); } static GrBuff *gizmo_create_buffer(Gizmo *gizmo, int sx, int sy, int w, int h) { if (!gizmo || !gizmo->_grBuff) return nullptr; GrBuff *grBuff = new GrBuff(w, h); Buffer *src = gizmo->_grBuff->get_buffer(); Buffer *dest = grBuff->get_buffer(); assert(src && dest); gr_buffer_rect_copy_2(src, dest, sx, sy, 0, 0, w, h); gizmo->_grBuff->release(); grBuff->release(); return grBuff; } static bool gizmo_item_contains(GizmoItem *item, int x, int y) { return item->_btnRect.contains(x, y); } static GizmoItem *gizmo_findItem(int id, Gizmo *gizmo) { for (GizmoItem *item = gizmo->_items; item; item = item->_next) { if (item->_id == id) return item; } return nullptr; } static bool gizmo_item_events(GizmoItem *item, int eventType, int event, int x, int y, GizmoItem **currentItem) { if (!item || !item->_button || eventType != EVENT_MOUSE) return false; GizmoButton *btn = item->_button; if (btn->_state == SELECTED) return false; bool flag1 = false; bool flag2 = false; bool flag3 = false; switch (event) { case _ME_move: if (gizmo_item_contains(item, x, y)) { if (!player_commands_allowed()) return false; *currentItem = item; if (btn->_state != IN_CONTROL) { btn->_state = IN_CONTROL; flag1 = true; } } else { *currentItem = nullptr; if (btn->_state != NOTHING) { btn->_state = NOTHING; flag1 = true; flag3 = false; } } break; case _ME_L_click: case _ME_doubleclick: if (gizmo_item_contains(item, x, y)) { if (!player_commands_allowed()) return false; btn->_state = OVER_CONTROL; *currentItem = item; flag1 = true; } else { *currentItem = nullptr; if (btn->_state != NOTHING) { btn->_state = NOTHING; flag1 = true; } } break; case _ME_L_hold: case _ME_doubleclick_hold: break; case _ME_L_drag: case _ME_doubleclick_drag: if (!*currentItem) return true; if (gizmo_item_contains(item, x, y)) { if (!player_commands_allowed()) return false; if (btn->_state != OVER_CONTROL) { btn->_state = OVER_CONTROL; flag1 = true; } } else { if (btn->_state != IN_CONTROL) { btn->_state = IN_CONTROL; flag1 = true; } } break; case _ME_L_release: case _ME_doubleclick_release: if (gizmo_item_contains(item, x, y)) { if (!player_commands_allowed()) return false; if (*currentItem) flag2 = true; else *currentItem = item; } else { *currentItem = nullptr; btn->_state = NOTHING; flag1 = true; flag3 = false; } break; default: break; } if (flag1) { (*item->_fnDraw)(item, item->_gizmo, item->_bounds.left, item->_bounds.top); int32 status; ScreenContext *ctx = vmng_screen_find(item->_gizmo, &status); if (ctx && status == 1) RestoreScreens(item->_bounds.left + ctx->x1, item->_bounds.top + ctx->y1, item->_bounds.right + ctx->x1, item->_bounds.bottom + ctx->y1); } if (flag2 && item->_select) { (*item->_select)(); flag3 = true; int32 status; ScreenContext *ctx = vmng_screen_find(item->_gizmo, &status); if (ctx && status == 1) { if (!gizmo_findItem(item->_id, item->_gizmo)) *currentItem = nullptr; } else { *currentItem = nullptr; } } return flag3; } static GizmoItem *gizmo_add_item(Gizmo *gizmo, int id, int boundsX, int boundsY, int boundsW, int boundsH, int rect1X, int rect1Y, int rect1W, int rect1H, GizmoItemFnSelect select, int btnIndex, bool selected = false, bool hasBuffer = false, int arg9 = 0, GizmoItemFnEvents events = gizmo_item_events) { if (!gizmo) return nullptr; // Create new item GizmoItem *item = (GizmoItem *)mem_alloc(sizeof(GizmoItem), "*gui gizmo item"); if (!item) error("gizmo_add_item - Not enough emory to create item (%zu bytes)", sizeof(GizmoItem)); // Put the new item at the head of the list item->_next = gizmo->_items; item->_prior = nullptr; item->_gizmo = gizmo; if (gizmo->_items) gizmo->_items->_prior = item; gizmo->_items = item; // Set fields item->_id = id; item->_bounds = Common::Rect(boundsX, boundsY, boundsX + boundsW - 1, boundsY + boundsH - 1); item->_btnRect = Common::Rect(rect1X, rect1Y, rect1X + rect1W - 1, rect1Y + rect1H - 1); item->_select = select; item->_hasBuffer = hasBuffer; if (hasBuffer) { item->_grBuff = gizmo_create_buffer(gizmo, rect1X, rect1Y, rect1W, rect1H); } else { item->_grBuff = nullptr; } GizmoButton *btn = (GizmoButton *)mem_alloc(sizeof(GizmoButton), "*gizmo button"); if (!btn) error("gizmo_add_item - Not enough emory to create btn (%zu bytes)", sizeof(GizmoButton)); btn->_state = selected ? SELECTED : NOTHING; btn->_index = btnIndex; btn->_field8 = arg9; btn->_field10 = id - 1000; item->_button = btn; item->_fnDraw = gizmo_item_draw; item->_fnFree = gizmo_item_free; item->_fnEvents = events; (*item->_fnDraw)(item, gizmo, boundsX, boundsY); int32 status = 0; ScreenContext *ctx = vmng_screen_find(gizmo, &status); if (ctx && status == 1) { RestoreScreens(ctx->x1 + item->_bounds.left, ctx->y1 + item->_bounds.top, ctx->x1 + item->_bounds.right, ctx->y1 + item->_bounds.bottom); } return item; } static Gizmo *gui_create_gizmo(M4sprite *sprite, int sx, int sy, uint scrnFlags) { if (!sprite) return nullptr; Gizmo *gui = (Gizmo *)mem_alloc(sizeof(Gizmo), "*gui gizmo"); if (!gui) return nullptr; GrBuff *grBuff = new GrBuff(sprite->w, sprite->h); gui->_grBuff = grBuff; gui->_items = nullptr; gui->_fnEnter = nullptr; gui->_fnEscape = nullptr; gui->_fnEvents = gizmo_eventHandler; Buffer *dest = gui->_grBuff->get_buffer(); Buffer *src = _G(gameDrawBuff)->get_buffer(); if ((_G(gameDrawBuff)->h - sy) >= dest->h) { gr_buffer_rect_copy_2(src, dest, sx, sy, 0, 0, dest->w, dest->h); } else { gr_buffer_rect_copy_2(src, dest, sx, sy, 0, 0, dest->w, _G(gameDrawBuff)->h - sy); } _G(gameDrawBuff)->release(); if (sprite->sourceHandle) gizmo_draw_sprite(sprite, dest, 0, 0); gui->_grBuff->release(); ScreenContext *ctx = vmng_screen_create(sx, sy, sx + sprite->w, sy + sprite->h, 69, scrnFlags, gui, (RefreshFunc)gizmo_draw, gizmo_eventHandler); return ctx ? gui : nullptr; } void gizmo_anim(RGB8 *pal) { if (!_GIZMO(initialized)) gizmo_initialize(pal); if (!gizmo_load_sprites("500gizmo", 58)) return; Gizmo *gizmo = gui_create_gizmo(_GIZMO(sprites)[0], 0, 0, 505); _GIZMO(gui) = gizmo; assert(gizmo); gizmo_add_item(gizmo, 1, 234, 153, 178, 48, 251, 163, 144, 25, []() { gizmo_daemon(5000); }, 0); gizmo_add_item(gizmo, 2, 234, 178, 178, 48, 254, 188, 136, 25, []() { gizmo_daemon(5001); }, 1); gizmo_add_item(gizmo, 3, 235, 203, 178, 48, 256, 212, 128, 27, []() { gizmo_daemon(5002); }, 2); gizmo_add_item(gizmo, 6, 234, 230, 178, 48, 260, 239, 119, 25, []() { gizmo_daemon(5003); }, 3); gizmo_add_item(gizmo, 5, 234, 255, 178, 48, 264, 264, 109, 25, []() { gizmo_daemon(5004); }, 4); gizmo_add_item(gizmo, 6, 234, 278, 178, 48, 266, 289, 102, 25, []() { gizmo_daemon(5005); }, 5); gizmo_add_item(gizmo, 7, 234, 300, 178, 48, 268, 314, 96, 25, []() { gizmo_daemon(5006); }, 6); vmng_screen_show(gizmo); mouse_lock_sprite(0); } } // namespace GUI } // namespace Burger } // namespace M4