diff --git a/src/extensions/extension_hooks.cpp b/src/extensions/extension_hooks.cpp index 1d29ebb0c..3c71d96d9 100644 --- a/src/extensions/extension_hooks.cpp +++ b/src/extensions/extension_hooks.cpp @@ -107,6 +107,7 @@ #include "displayext_hooks.h" #include "sidebarext_hooks.h" +#include "mouseext_hooks.h" #include "initext_hooks.h" #include "mainloopext_hooks.h" @@ -243,6 +244,7 @@ void Extension_Hooks() DisplayClassExtension_Hooks(); SidebarClassExtension_Hooks(); + MouseClassExtension_Hooks(); /** * Various modules and functions. diff --git a/src/extensions/mouse/mouseext_hooks.cpp b/src/extensions/mouse/mouseext_hooks.cpp new file mode 100644 index 000000000..275c60160 --- /dev/null +++ b/src/extensions/mouse/mouseext_hooks.cpp @@ -0,0 +1,245 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file MOUSEEXT_HOOKS.CPP + * + * @author CCHyper + * + * @brief Contains the hooks for the extended MouseClass. + * + * @license Vinifera is free software: you can 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. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU 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 "mouseext_hooks.h" +#include "mouse.h" +#include "mousetype.h" +#include "vinifera_globals.h" +#include "wwmouse.h" +#include "debughandler.h" +#include "asserthandler.h" + +#include "hooker.h" +#include "hooker_macros.h" + + +/** + * A fake class for implementing new member functions which allow + * access to the "this" pointer of the intended class. + * + * @note: This must not contain a constructor or destructor! + * @note: All functions must be prefixed with "_" to prevent accidental virtualization. + */ +class MouseClassExt final : public MouseClass +{ + public: + void _AI(KeyNumType &input, Point2D &xy); + bool _Override_Mouse_Shape(MouseType mouse, bool wsmall = false); + void _Mouse_Small(bool wsmall = true); + int _Get_Mouse_Current_Frame(MouseType mouse, bool wsmall = false) const; + Point2D _Get_Mouse_Hotspot(MouseType mouse) const; + int _Get_Mouse_Start_Frame(MouseType mouse) const; + int _Get_Mouse_Frame_Count(MouseType mouse) const; +}; + + +/** + * Controls the sizing of the mouse. + * + * @author: 09/21/1995 JLB - Red Alert source code. + * CCHyper - Adjustments for Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +void MouseClassExt::_Mouse_Small(bool wsmall) +{ + //MouseStruct const * control = &MouseControl[CurrentMouseShape]; + MouseTypeClass const * control = MouseTypeClass::As_Pointer(CurrentMouseShape); + + if (IsSmall == wsmall) { + return; + } + + IsSmall = wsmall; + + int frame = Get_Mouse_Current_Frame(CurrentMouseShape, wsmall); + Point2D hotspot = Get_Mouse_Hotspot(CurrentMouseShape); + + WWMouse->Set_Cursor(&hotspot, MouseShapes, frame); +} + + +/** + * Alters the shape of the mouse. + * + * @author: 03/10/1994 JLB - Red Alert source code. + * CCHyper - Adjustments for Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +bool MouseClassExt::_Override_Mouse_Shape(MouseType mouse, bool wsmall) +{ + ASSERT((unsigned)mouse < MOUSE_COUNT); + + //MouseStruct const * control = &MouseControl[mouse]; + MouseTypeClass const * control = MouseTypeClass::As_Pointer(mouse); + static bool startup = false; + int baseshp; + + /** + * Only certain mouse shapes have a small counterpart. If the requested mouse + * shape is not one of these, then force the small size override flag to false. + */ + if (control->SmallFrame == -1) { + wsmall = false; + } + + /** + * If the mouse shape is going to change, then inform the mouse driver of the + * change. + */ + if (!startup || (MouseShapes && ((mouse != CurrentMouseShape) || (wsmall != IsSmall)))) { + startup = true; + + Timer = control->FrameRate; + Frame = 0; + + baseshp = Get_Mouse_Current_Frame(mouse, wsmall); + Point2D hotspot = Get_Mouse_Hotspot(mouse); + WWMouse->Set_Cursor(&hotspot, MouseShapes, baseshp); + CurrentMouseShape = mouse; + IsSmall = wsmall; + return true; + } + return false; +} + + +/** + * Process player input as it relates to the mouse. + * + * @author: 12/24/1994 JLB - Red Alert source code. + * CCHyper - Adjustments for Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +void MouseClassExt::_AI(KeyNumType &input, Point2D &xy) +{ + //MouseStruct const * control = &MouseControl[CurrentMouseShape]; + MouseTypeClass const * control = MouseTypeClass::As_Pointer(CurrentMouseShape); + + if (control->FrameRate && Timer == 0) { + + Frame++; + Frame %= control->FrameCount; + Timer = control->FrameRate; + int baseframe = Get_Mouse_Current_Frame(CurrentMouseShape, IsSmall); + Point2D hotspot = Get_Mouse_Hotspot(CurrentMouseShape); + WWMouse->Set_Cursor(&hotspot, MouseShapes, baseframe); + } + + ScrollClass::AI(input, xy); +} + + +/** + * x + * + * @author: CCHyper - Reimplemented from Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +int MouseClassExt::_Get_Mouse_Current_Frame(MouseType mouse, bool wsmall) const +{ + //MouseStruct const * control = &MouseControl[mouse]; + MouseTypeClass const * control = MouseTypeClass::As_Pointer(mouse); + + if (wsmall) { + if (control->SmallFrame != -1) { + return control->SmallFrame + Frame; + } + } + + return control->StartFrame + Frame; +} + + +/** + * x + * + * @author: CCHyper - Reimplemented from Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +Point2D MouseClassExt::_Get_Mouse_Hotspot(MouseType mouse) const +{ + Point2D hotspot(0,0); + + //MouseStruct const * control = &MouseControl[mouse]; + MouseTypeClass const * control = MouseTypeClass::As_Pointer(mouse); + + int hotspot_x = control->Hotspot.X; + if (hotspot_x == MOUSE_HOTSPOT_CENTER) { + hotspot.X = MouseShapes->Get_Width() / 2; + } + if (hotspot_x == MOUSE_HOTSPOT_MAX) { + hotspot.X = MouseShapes->Get_Width(); + } + + int hotspot_y = control->Hotspot.Y; + if (hotspot_y == MOUSE_HOTSPOT_CENTER) { + hotspot.Y = MouseShapes->Get_Height() / 2; + } + if (hotspot_y == MOUSE_HOTSPOT_MAX) { + hotspot.Y = MouseShapes->Get_Height(); + } + + return hotspot; +} + + +/** + * Returns the starting frame of the mouse. + * + * @author: CCHyper - Reimplemented from Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +int MouseClassExt::_Get_Mouse_Start_Frame(MouseType mouse) const +{ + //return MouseControl[mouse].StartFrame; + return MouseTypeClass::As_Pointer(mouse)->StartFrame; +} + + +/** + * Returns the frame count of the mouse. + * + * @author: CCHyper - Reimplemented from Tiberian Sun. + * CCHyper - Change use of MouseControl to MouseTypes. + */ +int MouseClassExt::_Get_Mouse_Frame_Count(MouseType mouse) const +{ + //return MouseControl[mouse].FrameCount; + return MouseTypeClass::As_Pointer(mouse)->FrameCount; +} + + +void MouseClassExtension_Hooks() +{ + Patch_Jump(0x00562200, &MouseClassExt::_Mouse_Small); + Patch_Jump(0x005622D0, &MouseClassExt::_Get_Mouse_Current_Frame); + Patch_Jump(0x00562310, &MouseClassExt::_Get_Mouse_Hotspot); + Patch_Jump(0x00562390, &MouseClassExt::_Override_Mouse_Shape); + Patch_Jump(0x005624D0, &MouseClassExt::_AI); + Patch_Jump(0x00563220, &MouseClassExt::_Get_Mouse_Start_Frame); + Patch_Jump(0x00563240, &MouseClassExt::_Get_Mouse_Frame_Count); +} diff --git a/src/extensions/mouse/mouseext_hooks.h b/src/extensions/mouse/mouseext_hooks.h new file mode 100644 index 000000000..cfc555e94 --- /dev/null +++ b/src/extensions/mouse/mouseext_hooks.h @@ -0,0 +1,31 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file MOUSEEXT_HOOKS.H + * + * @author CCHyper + * + * @brief Contains the hooks for the extended MouseClass. + * + * @license Vinifera is free software: you can 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. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU 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 . + * + ******************************************************************************/ +#pragma once + + +void MouseClassExtension_Hooks(); diff --git a/src/new/mousetype/mousetype.cpp b/src/new/mousetype/mousetype.cpp new file mode 100644 index 000000000..90f6cca0d --- /dev/null +++ b/src/new/mousetype/mousetype.cpp @@ -0,0 +1,446 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file MOUSETYPE.CPP + * + * @author CCHyper + * + * @brief Mouse cursor controls and overrides. + * + * @license Vinifera is free software: you can 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. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU 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 StartFrame = std::strtol(tok, nullptr, 10); + + tok = std::strtok(nullptr, ","); + mousectrl->FrameCount = std::strtol(tok, nullptr, 10); + + tok = std::strtok(nullptr, ","); + mousectrl->FrameRate = std::strtol(tok, nullptr, 10); + + tok = std::strtok(nullptr, ","); + mousectrl->SmallFrame = std::strtol(tok, nullptr, 10); + + tok = std::strtok(nullptr, ","); + if (!strcmpi(tok, "left")) { + value = MOUSE_HOTSPOT_MIN; + } else if (!strcmpi(tok, "center")) { + value = MOUSE_HOTSPOT_CENTER; + } else if (!strcmpi(tok, "right")) { + value = MOUSE_HOTSPOT_MAX; + } else { + value = std::strtol(tok, nullptr, 10); + } + mousectrl->Hotspot.X = value; + + tok = std::strtok(nullptr, ","); + if (!strcmpi(tok, "top")) { + value = MOUSE_HOTSPOT_MIN; + } else if (!strcmpi(tok, "middle")) { + value = MOUSE_HOTSPOT_CENTER; + } else if (!strcmpi(tok, "bottom")) { + value = MOUSE_HOTSPOT_MAX; + } else { + value = std::strtol(tok, nullptr, 10); + } + mousectrl->Hotspot.Y = value; + + } + + return true; +} + + +#ifndef NDEBUG +/** + * Writes out the default mouse control values. + * + * @author: CCHyper + */ +bool MouseTypeClass::Write_Default_Mouse_INI(CCINIClass &ini) +{ + static char const * const MOUSE = "MouseTypes"; + + char buffer[1024]; + + for (MouseType mouse = MOUSE_NORMAL; mouse < MOUSE_COUNT; ++mouse) { + + MouseTypeClass &mousectrl = MouseControl[mouse]; + + const char *hotspot_x = nullptr; + const char *hotspot_y = nullptr; + + switch (mousectrl.Hotspot.X) { + case MOUSE_HOTSPOT_MIN: + hotspot_x = "left"; + break; + case MOUSE_HOTSPOT_CENTER: + hotspot_x = "center"; + break; + case MOUSE_HOTSPOT_MAX: + hotspot_x = "right"; + break; + default: + DEBUG_ERROR("Mouse: Invalid hotspot X for %s!\n", MouseNames[mouse]); + return false; + }; + + switch (mousectrl.Hotspot.Y) { + case MOUSE_HOTSPOT_MIN: + hotspot_y = "top"; + break; + case MOUSE_HOTSPOT_CENTER: + hotspot_y = "middle"; + break; + case MOUSE_HOTSPOT_MAX: + hotspot_y = "bottom"; + break; + default: + DEBUG_ERROR("Mouse: Invalid hotspot Y for %s!\n", MouseNames[mouse]); + return false; + }; + + std::snprintf(buffer, sizeof(buffer), "%d,%d,%d,%d,%s,%s", + mousectrl.StartFrame, + mousectrl.FrameCount, + mousectrl.FrameRate, + mousectrl.SmallFrame, + hotspot_x, + hotspot_y); + + ini.Put_String(MOUSE, MouseNames[mouse], buffer); + } + + return true; +} +#endif + + +/** + * Converts a mouse number into a mouse control object pointer. + * + * @author: CCHyper + */ +const MouseTypeClass *MouseTypeClass::As_Pointer(MouseType type) +{ + //ASSERT(type >= MOUSE_NORMAL && type < MouseTypes.Count()); + return type >= MOUSE_NORMAL && type < MouseTypes.Count() ? MouseTypes[type] : nullptr; +} + + +/** + * Converts a mouse name into a mouse control object pointer. + * + * @author: CCHyper + */ +const MouseTypeClass *MouseTypeClass::As_Pointer(const char *name) +{ + return As_Pointer(From_Name(name)); +} + + +/** + * Retrieves the mouse type for given name. + * + * @author: CCHyper + */ +MouseType MouseTypeClass::From_Name(const char *name) +{ + ASSERT(name != nullptr); + + if (Wstring(name) == "" || Wstring(name) == "none") { + return MOUSE_NORMAL; + } + + if (name != nullptr) { + for (MouseType index = MOUSE_NORMAL; index < MouseTypes.Count(); ++index) { + if (Wstring(MouseNames[index]) == name) { + return index; + } + } + } + + return MOUSE_NORMAL; +} + + +/** + * Returns name for given mouse control type. + * + * @author: CCHyper + */ +const char *MouseTypeClass::Name_From(MouseType type) +{ + return (type >= MOUSE_NORMAL && type < MouseTypes.Count() ? MouseNames[type] : ""); +} diff --git a/src/new/mousetype/mousetype.h b/src/new/mousetype/mousetype.h new file mode 100644 index 000000000..934f5f1f4 --- /dev/null +++ b/src/new/mousetype/mousetype.h @@ -0,0 +1,93 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file MOUSETYPE.H + * + * @author CCHyper + * + * @brief Mouse cursor controls and overrides. + * + * @license Vinifera is free software: you can 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. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU 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 . + * + ******************************************************************************/ +#pragma once + +#include "always.h" +#include "iomap.h" +#include "point.h" + + +class CCINIClass; +class NoInitClass; + + +/** + * This type is used to control the frames and rates of the mouse + * pointer. Some mouse pointers are actually looping animations. + */ +class MouseTypeClass +{ + friend class MouseClassExt; + + public: + MouseTypeClass(int start_frame, int frame_count, int frame_rate, int small_frame, Point2D hotspot); + MouseTypeClass(const NoInitClass &noinit); + virtual ~MouseTypeClass(); + + static void One_Time(); + + static bool Read_Mouse_INI(CCINIClass &ini); +#ifndef NDEBUG + static bool Write_Default_Mouse_INI(CCINIClass &ini); +#endif + + static const MouseTypeClass *As_Pointer(MouseType type); + static const MouseTypeClass *As_Pointer(const char *name); + static MouseType From_Name(const char *name); + static const char *Name_From(MouseType type); + + private: + /** + * Starting frame number. + */ + int StartFrame; + + /** + * Number of animation frames. + */ + int FrameCount; + + /** + * Frame delay between changing frames. + */ + int FrameRate; + + /** + * Start frame number for small version (if any). + */ + int SmallFrame; + + /** + * Hotspot X and Y offset. + */ + Point2D Hotspot; + + private: + static MouseTypeClass MouseControl[MOUSE_COUNT]; + static const char *MouseTypeClass::MouseNames[MOUSE_COUNT]; +}; diff --git a/src/vinifera/vinifera_functions.cpp b/src/vinifera/vinifera_functions.cpp index f1a0fbce9..946f2ab6f 100644 --- a/src/vinifera/vinifera_functions.cpp +++ b/src/vinifera/vinifera_functions.cpp @@ -49,6 +49,7 @@ #include "extension.h" #include "theatertype.h" #include "uicontrol.h" +#include "mousetype.h" #include "debughandler.h" #include "asserthandler.h" #include @@ -639,6 +640,7 @@ bool Vinifera_Shutdown() */ EBoltClass::Clear_All(); TheaterTypes.Clear(); + MouseTypes.Clear(); /** * Cleanup global extension instances. @@ -697,6 +699,44 @@ int Vinifera_Pre_Init_Game(int argc, char *argv[]) Vinifera_SkipStartupMovies = true; #endif + /** + * Read the mouse controls and overrides. + * + * This must be loaded before Init_Game as MouseClass::Override_Mouse_Shape + * is called as part of the games initialisation. + */ + MouseTypeClass::One_Time(); + +#ifndef NDEBUG + /** + * Write the default mouse control values to ini. + */ + { + CCFileClass mouse_write_file("MOUSE.DBG"); + CCINIClass mouse_write_ini; + mouse_write_file.Delete(); + MouseTypeClass::Write_Default_Mouse_INI(mouse_write_ini); + mouse_write_ini.Save(mouse_write_file, false); + mouse_write_file.Close(); + } +#endif + + CCFileClass mouse_file("MOUSE.INI"); + + if (mouse_file.Is_Available()) { + + CCINIClass mouse_ini; + mouse_ini.Load(mouse_file, false); + + if (!MouseTypeClass::Read_Mouse_INI(mouse_ini)) { + DEV_DEBUG_ERROR("Failed to read MOUSE.INI!\n"); + return EXIT_FAILURE; + } + + } else { + DEV_DEBUG_WARNING("MOUSE.INI not found!\n"); + } + return EXIT_SUCCESS; } diff --git a/src/vinifera/vinifera_globals.cpp b/src/vinifera/vinifera_globals.cpp index 0dc2bc495..32c3b8014 100644 --- a/src/vinifera/vinifera_globals.cpp +++ b/src/vinifera/vinifera_globals.cpp @@ -68,6 +68,7 @@ DynamicVectorClass ViniferaMoviesMixes; DynamicVectorClass EBolts; DynamicVectorClass TheaterTypes; +DynamicVectorClass MouseTypes; MFCC *GenericMix = nullptr; MFCC *IsoGenericMix = nullptr; diff --git a/src/vinifera/vinifera_globals.h b/src/vinifera/vinifera_globals.h index 79abf62da..bc3d065fe 100644 --- a/src/vinifera/vinifera_globals.h +++ b/src/vinifera/vinifera_globals.h @@ -34,6 +34,7 @@ class EBoltClass; class TheaterTypeClass; +class MouseTypeClass; extern bool Vinifera_DeveloperMode; @@ -95,6 +96,7 @@ extern MFCC *IsoGenericMix; */ extern DynamicVectorClass EBolts; extern DynamicVectorClass TheaterTypes; +extern DynamicVectorClass MouseTypes; /**