/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can 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 "ags/engine/ac/mouse.h" #include "ags/shared/ac/common.h" #include "ags/shared/ac/character_info.h" #include "ags/engine/ac/draw.h" #include "ags/engine/ac/dynobj/script_mouse.h" #include "ags/engine/ac/dynobj/script_system.h" #include "ags/engine/ac/game.h" #include "ags/engine/ac/game_setup.h" #include "ags/shared/ac/game_setup_struct.h" #include "ags/engine/ac/game_state.h" #include "ags/engine/ac/global_mouse.h" #include "ags/engine/ac/global_screen.h" #include "ags/engine/ac/sys_events.h" #include "ags/engine/ac/system.h" #include "ags/engine/ac/view_frame.h" #include "ags/engine/debugging/debug_log.h" #include "ags/shared/gui/gui_button.h" #include "ags/shared/gui/gui_main.h" #include "ags/engine/device/mouse_w32.h" #include "ags/shared/ac/sprite_cache.h" #include "ags/engine/gfx/graphics_driver.h" #include "ags/engine/gfx/gfxfilter.h" #include "ags/engine/platform/base/ags_platform_driver.h" #include "ags/shared/debugging/out.h" #include "ags/engine/script/script_api.h" #include "ags/engine/script/script_runtime.h" #include "ags/engine/ac/global_game.h" #include "ags/plugins/ags_plugin.h" #include "ags/globals.h" namespace AGS3 { using namespace AGS::Shared; using namespace AGS::Engine; // The mouse functions are static so the script doesn't pass // in an object parameter void Mouse_SetVisible(int isOn) { if (isOn) ShowMouseCursor(); else HideMouseCursor(); } int Mouse_GetVisible() { if (_GP(play).mouse_cursor_hidden) return 0; return 1; } void SetMouseBounds(int x1, int y1, int x2, int y2) { int xmax = game_to_data_coord(_GP(play).GetMainViewport().GetWidth()) - 1; int ymax = game_to_data_coord(_GP(play).GetMainViewport().GetHeight()) - 1; if ((x1 == 0) && (y1 == 0) && (x2 == 0) && (y2 == 0)) { x2 = xmax; y2 = ymax; } else { if (x1 < 0 || x1 > xmax || x2 < 0 || x2 > xmax || x1 > x2 || y1 < 0 || y1 > ymax || y2 < 0 || y2 > ymax || y1 > y2) debug_script_warn("SetMouseBounds: arguments are out of range and will be corrected: (%d,%d)-(%d,%d), range is (%d,%d)-(%d,%d)", x1, y1, x2, y2, 0, 0, xmax, ymax); x1 = Math::Clamp(x1, 0, xmax); x2 = Math::Clamp(x2, x1, xmax); y1 = Math::Clamp(y1, 0, ymax); y2 = Math::Clamp(y2, y1, ymax); } debug_script_log("Mouse bounds constrained to (%d,%d)-(%d,%d)", x1, y1, x2, y2); data_to_game_coords(&x1, &y1); data_to_game_round_up(&x2, &y2); _GP(play).mboundx1 = x1; _GP(play).mboundx2 = x2; _GP(play).mboundy1 = y1; _GP(play).mboundy2 = y2; _GP(mouse).SetMoveLimit(Rect(x1, y1, x2, y2)); } // mouse cursor functions: // set_mouse_cursor: changes visual appearance to specified cursor void set_mouse_cursor(int newcurs, bool force_update) { const int hotspotx = _GP(game).mcurs[newcurs].hotx, hotspoty = _GP(game).mcurs[newcurs].hoty; _GP(mouse).SetHotspot(hotspotx, hotspoty); // if it's same cursor and there's animation in progress, then don't assign a new pic just yet if (!force_update && newcurs == _G(cur_cursor) && _GP(game).mcurs[newcurs].view >= 0 && (_G(mouse_frame) > 0 || _G(mouse_delay) > 0)) { return; } // reset animation timing only if it's another cursor if (newcurs != _G(cur_cursor)) { _G(cur_cursor) = newcurs; _G(mouse_frame) = 0; _G(mouse_delay) = 0; } // Assign new pic set_new_cursor_graphic(_GP(game).mcurs[newcurs].pic); delete _G(dotted_mouse_cursor); _G(dotted_mouse_cursor) = nullptr; // If it's inventory cursor, draw hotspot crosshair sprite upon it if ((newcurs == MODE_USE) && (_GP(game).mcurs[newcurs].pic > 0) && ((_GP(game).hotdot > 0) || (_GP(game).invhotdotsprite > 0))) { // If necessary, create a copy of the cursor and put the hotspot // dot onto it _G(dotted_mouse_cursor) = BitmapHelper::CreateBitmapCopy(_G(mousecurs)[0]); if (_GP(game).invhotdotsprite > 0) { draw_sprite_slot_support_alpha(_G(dotted_mouse_cursor), (_GP(game).SpriteInfos[_GP(game).mcurs[newcurs].pic].Flags & SPF_ALPHACHANNEL) != 0, hotspotx - _GP(game).SpriteInfos[_GP(game).invhotdotsprite].Width / 2, hotspoty - _GP(game).SpriteInfos[_GP(game).invhotdotsprite].Height / 2, _GP(game).invhotdotsprite); } else { putpixel_scaled(_G(dotted_mouse_cursor), hotspotx, hotspoty, MakeColor(_GP(game).hotdot)); if (_GP(game).hotdotouter > 0) { const int outercol = MakeColor(_GP(game).hotdotouter); putpixel_scaled(_G(dotted_mouse_cursor), hotspotx + get_fixed_pixel_size(1), hotspoty, outercol); putpixel_scaled(_G(dotted_mouse_cursor), hotspotx, hotspoty + get_fixed_pixel_size(1), outercol); putpixel_scaled(_G(dotted_mouse_cursor), hotspotx - get_fixed_pixel_size(1), hotspoty, outercol); putpixel_scaled(_G(dotted_mouse_cursor), hotspotx, hotspoty - get_fixed_pixel_size(1), outercol); } } _G(mousecurs)[0] = _G(dotted_mouse_cursor); update_cached_mouse_cursor(); } } // set_default_cursor: resets visual appearance to current mode (walk, look, etc) void set_default_cursor() { set_mouse_cursor(_G(cur_mode)); } // permanently change cursor graphic void ChangeCursorGraphic(int curs, int newslot) { if ((curs < 0) || (curs >= _GP(game).numcursors)) quit("!ChangeCursorGraphic: invalid mouse cursor"); if ((curs == MODE_USE) && (_GP(game).options[OPT_FIXEDINVCURSOR] == 0)) debug_script_warn("Mouse.ChangeModeGraphic should not be used on the Inventory cursor when the cursor is linked to the active inventory item"); _GP(game).mcurs[curs].pic = newslot; _GP(spriteset).PrecacheSprite(newslot); if (curs == _G(cur_mode)) set_mouse_cursor(curs); } int Mouse_GetModeGraphic(int curs) { if ((curs < 0) || (curs >= _GP(game).numcursors)) quit("!Mouse.GetModeGraphic: invalid mouse cursor"); return _GP(game).mcurs[curs].pic; } void ChangeCursorHotspot(int curs, int x, int y) { if ((curs < 0) || (curs >= _GP(game).numcursors)) quit("!ChangeCursorHotspot: invalid mouse cursor"); _GP(game).mcurs[curs].hotx = data_to_game_coord(x); _GP(game).mcurs[curs].hoty = data_to_game_coord(y); if (curs == _G(cur_cursor)) set_mouse_cursor(_G(cur_cursor)); } void Mouse_ChangeModeView(int curs, int newview, int delay) { if ((curs < 0) || (curs >= _GP(game).numcursors)) quit("!Mouse.ChangeModeView: invalid mouse cursor"); newview--; _GP(game).mcurs[curs].view = newview; if (delay != SCR_NO_VALUE) _GP(game).mcurs[curs].animdelay = delay; if (newview >= 0) { precache_view(newview); } if (curs == _G(cur_cursor)) _G(mouse_delay) = 0; // force update } void Mouse_ChangeModeView2(int curs, int newview) { Mouse_ChangeModeView(curs, newview, SCR_NO_VALUE); } void SetNextCursor() { set_cursor_mode(find_next_enabled_cursor(_G(cur_mode) + 1)); } void SetPreviousCursor() { set_cursor_mode(find_previous_enabled_cursor(_G(cur_mode) - 1)); } // set_cursor_mode: changes mode and appearance void set_cursor_mode(int newmode) { if ((newmode < 0) || (newmode >= _GP(game).numcursors)) quit("!SetCursorMode: invalid cursor mode specified"); if (_GP(game).mcurs[newmode].flags & MCF_DISABLED) { find_next_enabled_cursor(newmode); return; } if (newmode == MODE_USE) { if (_G(playerchar)->activeinv == -1) { find_next_enabled_cursor(0); return; } update_inv_cursor(_G(playerchar)->activeinv); } _G(cur_mode) = newmode; set_default_cursor(); debug_script_log("Cursor mode set to %d", newmode); } void enable_cursor_mode(int modd) { if (modd < 0 || modd >= (int)_GP(game).mcurs.size()) { warning("Attempt to enable invalid cursor (%d), ignoring", modd); return; } _GP(game).mcurs[modd].flags &= ~MCF_DISABLED; // now search the interfaces for related buttons to re-enable int uu, ww; for (uu = 0; uu < _GP(game).numgui; uu++) { for (ww = 0; ww < _GP(guis)[uu].GetControlCount(); ww++) { if (_GP(guis)[uu].GetControlType(ww) != kGUIButton) continue; GUIButton *gbpt = (GUIButton *)_GP(guis)[uu].GetControl(ww); if (gbpt->ClickAction[kGUIClickLeft] != kGUIAction_SetMode) continue; if (gbpt->ClickData[kGUIClickLeft] != modd) continue; gbpt->SetEnabled(true); } } } void disable_cursor_mode(int modd) { _GP(game).mcurs[modd].flags |= MCF_DISABLED; // now search the interfaces for related buttons to kill int uu, ww; for (uu = 0; uu < _GP(game).numgui; uu++) { for (ww = 0; ww < _GP(guis)[uu].GetControlCount(); ww++) { if (_GP(guis)[uu].GetControlType(ww) != kGUIButton) continue; GUIButton *gbpt = (GUIButton *)_GP(guis)[uu].GetControl(ww); if (gbpt->ClickAction[kGUIClickLeft] != kGUIAction_SetMode) continue; if (gbpt->ClickData[kGUIClickLeft] != modd) continue; gbpt->SetEnabled(false); } } if (_G(cur_mode) == modd) find_next_enabled_cursor(modd); } void RefreshMouse() { ags_domouse(); _GP(scmouse).x = game_to_data_coord(_G(mousex)); _GP(scmouse).y = game_to_data_coord(_G(mousey)); } void SetMousePosition(int newx, int newy) { const Rect &viewport = _GP(play).GetMainViewport(); if (newx < 0) newx = 0; if (newy < 0) newy = 0; if (newx >= viewport.GetWidth()) newx = viewport.GetWidth() - 1; if (newy >= viewport.GetHeight()) newy = viewport.GetHeight() - 1; data_to_game_coords(&newx, &newy); _GP(mouse).SetPosition(Point(newx, newy)); RefreshMouse(); } int GetCursorMode() { return _G(cur_mode); } int IsButtonDown(int which) { if ((which < kMouseLeft) || (which > kMouseMiddle)) quit("!IsButtonDown: only works with eMouseLeft, eMouseRight, eMouseMiddle"); return ags_misbuttondown(static_cast(which)) ? 1 : 0; } int IsModeEnabled(int which) { return (which < 0) || (which >= _GP(game).numcursors) ? 0 : which == MODE_USE ? _G(playerchar)->activeinv > 0 : (_GP(game).mcurs[which].flags & MCF_DISABLED) == 0; } void SimulateMouseClick(int button_id) { _G(simulatedClick) = static_cast(button_id); } void Mouse_EnableControl(bool on) { bool should_control_mouse = _GP(usetup).mouse_ctrl_when == kMouseCtrl_Always || (_GP(usetup).mouse_ctrl_when == kMouseCtrl_Fullscreen && (_GP(scsystem).windowed == 0)); _GP(mouse).SetMovementControl(should_control_mouse & on); _GP(usetup).mouse_ctrl_enabled = on; // remember setting in config } bool Mouse_GetAutoLock() { return _GP(usetup).mouse_auto_lock; } void Mouse_SetAutoLock(bool on) { _GP(usetup).mouse_auto_lock = on; if (_GP(scsystem).windowed) { if (_GP(usetup).mouse_auto_lock) _GP(mouse).TryLockToWindow(); else _GP(mouse).UnlockFromWindow(); } } //============================================================================= int GetMouseCursor() { return _G(cur_cursor); } void update_script_mouse_coords() { _GP(scmouse).x = game_to_data_coord(_G(mousex)); _GP(scmouse).y = game_to_data_coord(_G(mousey)); } void update_inv_cursor(int invnum) { if ((_GP(game).options[OPT_FIXEDINVCURSOR] == 0) && (invnum > 0)) { int cursorSprite = _GP(game).invinfo[invnum].cursorPic; // Fall back to the inventory pic if no cursor pic is defined. if (cursorSprite == 0) cursorSprite = _GP(game).invinfo[invnum].pic; _GP(game).mcurs[MODE_USE].pic = cursorSprite; // all cursor images must be pre-cached _GP(spriteset).PrecacheSprite(cursorSprite); if ((_GP(game).invinfo[invnum].hotx > 0) || (_GP(game).invinfo[invnum].hoty > 0)) { // if the hotspot was set (unfortunately 0,0 isn't a valid co-ord) _GP(game).mcurs[MODE_USE].hotx = _GP(game).invinfo[invnum].hotx; _GP(game).mcurs[MODE_USE].hoty = _GP(game).invinfo[invnum].hoty; } else { _GP(game).mcurs[MODE_USE].hotx = _GP(game).SpriteInfos[cursorSprite].Width / 2; _GP(game).mcurs[MODE_USE].hoty = _GP(game).SpriteInfos[cursorSprite].Height / 2; } } } void update_cached_mouse_cursor() { if (_G(mouseCursor) != nullptr) _G(gfxDriver)->DestroyDDB(_G(mouseCursor)); _G(mouseCursor) = _G(gfxDriver)->CreateDDBFromBitmap(_G(mousecurs)[0], _G(alpha_blend_cursor) != 0); } void set_new_cursor_graphic(int spriteslot) { _G(mousecurs)[0] = _GP(spriteset)[spriteslot]; // It looks like spriteslot 0 can be used in games with version 2.72 and lower. // The NULL check should ensure that the sprite is valid anyway. if (((spriteslot < 1) && (_G(loaded_game_file_version) > kGameVersion_272)) || (_G(mousecurs)[0] == nullptr)) { if (_G(blank_mouse_cursor) == nullptr) { _G(blank_mouse_cursor) = BitmapHelper::CreateTransparentBitmap(1, 1, _GP(game).GetColorDepth()); } _G(mousecurs)[0] = _G(blank_mouse_cursor); } if (_GP(game).SpriteInfos[spriteslot].Flags & SPF_ALPHACHANNEL) _G(alpha_blend_cursor) = 1; else _G(alpha_blend_cursor) = 0; update_cached_mouse_cursor(); } bool is_standard_cursor_enabled(int curs) { if ((_GP(game).mcurs[curs].flags & MCF_DISABLED) == 0) { // inventory cursor, and they have an active item if (curs == MODE_USE) { if (_G(playerchar)->activeinv > 0) return true; } // standard cursor that's not disabled, go with it else if (_GP(game).mcurs[curs].flags & MCF_STANDARD) return true; } return false; } int find_next_enabled_cursor(int startwith) { if (startwith >= _GP(game).numcursors) startwith = 0; int testing = startwith; do { if (is_standard_cursor_enabled(testing)) break; testing++; if (testing >= _GP(game).numcursors) testing = 0; } while (testing != startwith); if (testing != startwith) set_cursor_mode(testing); return testing; } int find_previous_enabled_cursor(int startwith) { if (startwith < 0) startwith = _GP(game).numcursors - 1; int testing = startwith; do { if (is_standard_cursor_enabled(testing)) break; testing--; if (testing < 0) testing = _GP(game).numcursors - 1; } while (testing != startwith); if (testing != startwith) set_cursor_mode(testing); return testing; } //============================================================================= // // Script API Functions // //============================================================================= // void (int curs, int newslot) RuntimeScriptValue Sc_ChangeCursorGraphic(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT2(ChangeCursorGraphic); } // void (int curs, int x, int y) RuntimeScriptValue Sc_ChangeCursorHotspot(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT3(ChangeCursorHotspot); } // void (int curs, int newview) RuntimeScriptValue Sc_Mouse_ChangeModeView2(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT2(Mouse_ChangeModeView2); } RuntimeScriptValue Sc_Mouse_ChangeModeView(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT3(Mouse_ChangeModeView); } // void (int modd) RuntimeScriptValue Sc_disable_cursor_mode(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT(disable_cursor_mode); } // void (int modd) RuntimeScriptValue Sc_enable_cursor_mode(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT(enable_cursor_mode); } // int (int curs) RuntimeScriptValue Sc_Mouse_GetModeGraphic(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_INT_PINT(Mouse_GetModeGraphic); } // int (int which) RuntimeScriptValue Sc_IsButtonDown(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_INT_PINT(IsButtonDown); } // int (int which) RuntimeScriptValue Sc_IsModeEnabled(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_INT_PINT(IsModeEnabled); } // void (); RuntimeScriptValue Sc_SaveCursorForLocationChange(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID(SaveCursorForLocationChange); } // void () RuntimeScriptValue Sc_SetNextCursor(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID(SetNextCursor); } // void () RuntimeScriptValue Sc_SetPreviousCursor(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID(SetPreviousCursor); } // void (int x1, int y1, int x2, int y2) RuntimeScriptValue Sc_SetMouseBounds(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT4(SetMouseBounds); } // void (int newx, int newy) RuntimeScriptValue Sc_SetMousePosition(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT2(SetMousePosition); } // void () RuntimeScriptValue Sc_RefreshMouse(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID(RefreshMouse); } // void () RuntimeScriptValue Sc_set_default_cursor(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID(set_default_cursor); } // void (int newcurs) RuntimeScriptValue Sc_set_mouse_cursor(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT(set_mouse_cursor); } // int () RuntimeScriptValue Sc_GetCursorMode(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_INT(GetCursorMode); } // void (int newmode) RuntimeScriptValue Sc_set_cursor_mode(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT(set_cursor_mode); } // int () RuntimeScriptValue Sc_Mouse_GetVisible(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_INT(Mouse_GetVisible); } // void (int isOn) RuntimeScriptValue Sc_Mouse_SetVisible(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT(Mouse_SetVisible); } RuntimeScriptValue Sc_Mouse_Click(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PINT(SimulateMouseClick); } RuntimeScriptValue Sc_Mouse_GetControlEnabled(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_BOOL(_GP(mouse).IsControlEnabled); } RuntimeScriptValue Sc_Mouse_SetControlEnabled(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PBOOL(Mouse_EnableControl); } RuntimeScriptValue Sc_Mouse_GetAutoLock(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_BOOL(Mouse_GetAutoLock); } RuntimeScriptValue Sc_Mouse_SetAutoLock(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_VOID_PBOOL(Mouse_SetAutoLock); } RuntimeScriptValue Sc_Mouse_GetSpeed(const RuntimeScriptValue *params, int32_t param_count) { API_SCALL_FLOAT(_GP(mouse).GetSpeed); } RuntimeScriptValue Sc_Mouse_SetSpeed(const RuntimeScriptValue *params, int32_t param_count) { ASSERT_PARAM_COUNT("Mouse::Speed", 1); _GP(mouse).SetSpeed(params[0].FValue); return RuntimeScriptValue(); } void RegisterMouseAPI() { ScFnRegister mouse_api[] = { {"Mouse::ChangeModeGraphic^2", API_FN_PAIR(ChangeCursorGraphic)}, {"Mouse::ChangeModeHotspot^3", API_FN_PAIR(ChangeCursorHotspot)}, {"Mouse::ChangeModeView^2", API_FN_PAIR(Mouse_ChangeModeView2)}, {"Mouse::ChangeModeView^3", API_FN_PAIR(Mouse_ChangeModeView)}, {"Mouse::Click^1", Sc_Mouse_Click}, {"Mouse::DisableMode^1", API_FN_PAIR(disable_cursor_mode)}, {"Mouse::EnableMode^1", API_FN_PAIR(enable_cursor_mode)}, {"Mouse::GetModeGraphic^1", API_FN_PAIR(Mouse_GetModeGraphic)}, {"Mouse::IsButtonDown^1", API_FN_PAIR(IsButtonDown)}, {"Mouse::IsModeEnabled^1", API_FN_PAIR(IsModeEnabled)}, {"Mouse::SaveCursorUntilItLeaves^0", API_FN_PAIR(SaveCursorForLocationChange)}, {"Mouse::SelectNextMode^0", API_FN_PAIR(SetNextCursor)}, {"Mouse::SelectPreviousMode^0", API_FN_PAIR(SetPreviousCursor)}, {"Mouse::SetBounds^4", API_FN_PAIR(SetMouseBounds)}, {"Mouse::SetPosition^2", API_FN_PAIR(SetMousePosition)}, {"Mouse::Update^0", API_FN_PAIR(RefreshMouse)}, {"Mouse::UseDefaultGraphic^0", API_FN_PAIR(set_default_cursor)}, {"Mouse::UseModeGraphic^1", API_FN_PAIR(set_mouse_cursor)}, {"Mouse::get_AutoLock", API_FN_PAIR(Mouse_GetAutoLock)}, {"Mouse::set_AutoLock", API_FN_PAIR(Mouse_SetAutoLock)}, {"Mouse::get_ControlEnabled", Sc_Mouse_GetControlEnabled}, {"Mouse::set_ControlEnabled", Sc_Mouse_SetControlEnabled}, {"Mouse::get_Mode", API_FN_PAIR(GetCursorMode)}, {"Mouse::set_Mode", API_FN_PAIR(set_cursor_mode)}, {"Mouse::get_Speed", Sc_Mouse_GetSpeed}, {"Mouse::set_Speed", Sc_Mouse_SetSpeed}, {"Mouse::get_Visible", API_FN_PAIR(Mouse_GetVisible)}, {"Mouse::set_Visible", API_FN_PAIR(Mouse_SetVisible)}, }; ccAddExternalFunctions361(mouse_api); } } // namespace AGS3